From fb9fdc87d7186cb070b61c2b35560a524060b4d8 Mon Sep 17 00:00:00 2001 From: Beto Dealmeida Date: Wed, 13 Mar 2019 22:28:12 +0200 Subject: [PATCH 01/38] Show tooltip with time frame (#6979) --- .../components/controls/DateFilterControl.jsx | 34 ++++++++++++++----- 1 file changed, 25 insertions(+), 9 deletions(-) diff --git a/superset/assets/src/explore/components/controls/DateFilterControl.jsx b/superset/assets/src/explore/components/controls/DateFilterControl.jsx index 7fe0ce10c..0a8579620 100644 --- a/superset/assets/src/explore/components/controls/DateFilterControl.jsx +++ b/superset/assets/src/explore/components/controls/DateFilterControl.jsx @@ -32,6 +32,7 @@ import { Radio, Tab, Tabs, + Tooltip, } from 'react-bootstrap'; import Datetime from 'react-datetime'; import 'react-datetime/css/react-datetime.css'; @@ -311,15 +312,30 @@ export default class DateFilterControl extends React.Component { {grain} )); - const timeFrames = COMMON_TIME_FRAMES.map(timeFrame => ( - this.setState(getStateFromCommonTimeFrame(timeFrame))} - > - {timeFrame} - - )); + const timeFrames = COMMON_TIME_FRAMES.map((timeFrame) => { + const nextState = getStateFromCommonTimeFrame(timeFrame); + return ( + + {nextState.since}
{nextState.until} + + } + > +
+ this.setState(nextState)} + > + {timeFrame} + +
+
+ ); + }); return (
From 7488b4d32924aa8523423c3f245ecf5ab0d16276 Mon Sep 17 00:00:00 2001 From: Beto Dealmeida Date: Wed, 13 Mar 2019 22:28:44 +0200 Subject: [PATCH 02/38] Fix time filter control (#6978) --- superset/assets/src/explore/controls.jsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/superset/assets/src/explore/controls.jsx b/superset/assets/src/explore/controls.jsx index 9bb8198ca..fd0cede36 100644 --- a/superset/assets/src/explore/controls.jsx +++ b/superset/assets/src/explore/controls.jsx @@ -915,7 +915,7 @@ export const controls = { 'The options here are defined on a per database ' + 'engine basis in the Superset source code.'), mapStateToProps: state => ({ - choices: (state.datasource) ? state.datasource.time_grain_sqla : null, + choices: (state.datasource) ? state.datasource.timeGrainSqla : null, }), }, From 7d950365548b360ed3a31a9a0927e5f68fe695e0 Mon Sep 17 00:00:00 2001 From: Kim Truong <47833996+khtruong@users.noreply.github.com> Date: Tue, 19 Mar 2019 14:26:17 -0700 Subject: [PATCH 03/38] feat: add ability to change font size in big number (#7003) * Add ability to change font sizes in Big Number * rename big number to header * Add comment to clarify font size values --- superset/assets/backendSync.json | 58 +++++++++++++++++ .../src/explore/controlPanels/BigNumber.js | 13 +++- .../explore/controlPanels/BigNumberTotal.js | 13 +++- superset/assets/src/explore/controls.jsx | 62 +++++++++++++++++++ 4 files changed, 144 insertions(+), 2 deletions(-) diff --git a/superset/assets/backendSync.json b/superset/assets/backendSync.json index 3dfb57381..389da9cc2 100644 --- a/superset/assets/backendSync.json +++ b/superset/assets/backendSync.json @@ -2497,6 +2497,64 @@ "default": "150", "description": "Font size for the biggest value in the list" }, + "header_font_size": { + "type": "SelectControl", + "label": "Header Font Size", + "renderTrigger": true, + "clearable": false, + "default": 0.3, + "options": [ + { + "label": "Tiny", + "value": 0.125 + }, + { + "label": "Small", + "value": 0.2 + }, + { + "label": "Normal", + "value": 0.3 + }, + { + "label": "Large", + "value": 0.4 + }, + { + "label": "Huge", + "value": 0.5 + } + ] + }, + "subheader_font_size": { + "type": "SelectControl", + "label": "Subheader Font Size", + "renderTrigger": true, + "clearable": false, + "default": 0.125, + "options": [ + { + "label": "Tiny", + "value": 0.125 + }, + { + "label": "Small", + "value": 0.2 + }, + { + "label": "Normal", + "value": 0.3 + }, + { + "label": "Large", + "value": 0.4 + }, + { + "label": "Huge", + "value": 0.5 + } + ] + }, "instant_filtering": { "type": "CheckboxControl", "label": "Instant Filtering", diff --git a/superset/assets/src/explore/controlPanels/BigNumber.js b/superset/assets/src/explore/controlPanels/BigNumber.js index b931c7100..e621ee1f7 100644 --- a/superset/assets/src/explore/controlPanels/BigNumber.js +++ b/superset/assets/src/explore/controlPanels/BigNumber.js @@ -29,13 +29,21 @@ export default { ], }, { - label: t('Chart Options'), + label: t('Options'), expanded: true, controlSetRows: [ ['compare_lag', 'compare_suffix'], ['y_axis_format', null], ['show_trend_line', 'start_y_axis_at_zero'], + ], + }, + { + label: t('Chart Options'), + expanded: true, + controlSetRows: [ ['color_picker', null], + ['header_font_size'], + ['subheader_font_size'], ], }, ], @@ -43,5 +51,8 @@ export default { y_axis_format: { label: t('Number format'), }, + header_font_size: { + label: t('Big Number Font Size'), + }, }, }; diff --git a/superset/assets/src/explore/controlPanels/BigNumberTotal.js b/superset/assets/src/explore/controlPanels/BigNumberTotal.js index 22a7d6980..7b3e730ce 100644 --- a/superset/assets/src/explore/controlPanels/BigNumberTotal.js +++ b/superset/assets/src/explore/controlPanels/BigNumberTotal.js @@ -29,17 +29,28 @@ export default { ], }, { - label: t('Chart Options'), + label: t('Options'), expanded: true, controlSetRows: [ ['subheader'], ['y_axis_format'], ], }, + { + label: t('Chart Options'), + expanded: true, + controlSetRows: [ + ['header_font_size'], + ['subheader_font_size'], + ], + }, ], controlOverrides: { y_axis_format: { label: t('Number format'), }, + header_font_size: { + label: t('Big Number Font Size'), + }, }, }; diff --git a/superset/assets/src/explore/controls.jsx b/superset/assets/src/explore/controls.jsx index fd0cede36..ae73e801c 100644 --- a/superset/assets/src/explore/controls.jsx +++ b/superset/assets/src/explore/controls.jsx @@ -1400,6 +1400,68 @@ export const controls = { description: t('Font size for the biggest value in the list'), }, + header_font_size: { + type: 'SelectControl', + label: t('Header Font Size'), + renderTrigger: true, + clearable: false, + default: 0.3, + // Values represent the percentage of space a header should take + options: [ + { + label: t('Tiny'), + value: 0.125, + }, + { + label: t('Small'), + value: 0.2, + }, + { + label: t('Normal'), + value: 0.3, + }, + { + label: t('Large'), + value: 0.4, + }, + { + label: t('Huge'), + value: 0.5, + }, + ], + }, + + subheader_font_size: { + type: 'SelectControl', + label: t('Subheader Font Size'), + renderTrigger: true, + clearable: false, + default: 0.125, + // Values represent the percentage of space a subheader should take + options: [ + { + label: t('Tiny'), + value: 0.125, + }, + { + label: t('Small'), + value: 0.2, + }, + { + label: t('Normal'), + value: 0.3, + }, + { + label: t('Large'), + value: 0.4, + }, + { + label: t('Huge'), + value: 0.5, + }, + ], + }, + instant_filtering: { type: 'CheckboxControl', label: t('Instant Filtering'), From f66b598d37f183ba19cfa258eaae86d46dd7e5b2 Mon Sep 17 00:00:00 2001 From: Beto Dealmeida Date: Wed, 20 Mar 2019 16:56:30 -0700 Subject: [PATCH 04/38] Allow LIMIT to be specified in parameters (#7052) --- superset/views/core.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/superset/views/core.py b/superset/views/core.py index ad5153b26..39343d67c 100755 --- a/superset/views/core.py +++ b/superset/views/core.py @@ -2558,10 +2558,8 @@ class Superset(BaseSupersetView): ) client_id = request.form.get('client_id') or utils.shortid()[:10] - limits = [mydb.db_engine_spec.get_limit_from_sql(sql), limit] query = Query( database_id=int(database_id), - limit=min(lim for lim in limits if lim is not None), sql=sql, schema=schema, select_as_cta=request.form.get('select_as_cta') == 'true', @@ -2591,6 +2589,10 @@ class Superset(BaseSupersetView): return json_error_response( 'Template rendering failed: {}'.format(utils.error_msg_from_exception(e))) + # set LIMIT after template processing + limits = [mydb.db_engine_spec.get_limit_from_sql(rendered_query), limit] + query.limit = min(lim for lim in limits if lim is not None) + # Async request. if async_: logging.info('Running query on a Celery worker') From e974a23f90af3475025602cefc19dfda4037a0b0 Mon Sep 17 00:00:00 2001 From: Kim Truong <47833996+khtruong@users.noreply.github.com> Date: Fri, 29 Mar 2019 06:35:22 -0700 Subject: [PATCH 05/38] [Lyft-GA] Enable color consistency in a dashboard (#7135) * Enable color consistency in a dashboard Moved actions, minor UI, allowed dashboard copy Fix linting errors Undo unintentional change Updated and added unit tests Fail quietly if package has not been updated Fail quietly on dashboard copy if package is old * Update packages * Remove unnecessary code * Addressed Grace's comments * Small fix for item key * Reset chart's color during exploration * Do not reset chart form data when exploring chart --- superset/assets/package-lock.json | 6 +- .../components/DashboardBuilder_spec.jsx | 26 +++- .../dashboard/components/Header_spec.jsx | 8 +- .../dashboard/fixtures/mockDashboardState.js | 3 +- .../dashboard/reducers/dashboardState_spec.js | 25 ++-- superset/assets/src/chart/ChartRenderer.jsx | 3 +- .../src/dashboard/actions/dashboardLayout.js | 3 +- .../src/dashboard/actions/dashboardState.js | 18 ++- .../components/BuilderComponentPane.jsx | 112 +++++------------ .../components/ColorComponentPane.jsx | 107 ++++++++++++++++ .../dashboard/components/DashboardBuilder.jsx | 26 +++- .../src/dashboard/components/Header.jsx | 96 ++++++++++---- .../components/HeaderActionsDropdown.jsx | 11 +- .../components/InsertComponentPane.jsx | 118 ++++++++++++++++++ .../src/dashboard/components/SaveModal.jsx | 15 +++ .../assets/src/dashboard/containers/Chart.jsx | 3 +- .../dashboard/containers/DashboardBuilder.jsx | 11 +- .../dashboard/containers/DashboardHeader.jsx | 8 +- .../src/dashboard/reducers/dashboardState.js | 21 +++- .../src/dashboard/reducers/getInitialState.js | 20 ++- .../stylesheets/builder-sidepane.less | 14 +++ .../src/dashboard/stylesheets/dashboard.less | 9 +- .../charts/getFormDataWithExtraFilters.js | 4 + .../assets/src/dashboard/util/constants.js | 7 ++ .../assets/src/dashboard/util/propShapes.jsx | 5 +- .../controls/ColorSchemeControl.jsx | 28 +++-- superset/views/core.py | 6 + tests/dashboard_tests.py | 43 +++++++ 28 files changed, 586 insertions(+), 170 deletions(-) create mode 100644 superset/assets/src/dashboard/components/ColorComponentPane.jsx create mode 100644 superset/assets/src/dashboard/components/InsertComponentPane.jsx diff --git a/superset/assets/package-lock.json b/superset/assets/package-lock.json index 17a4f3356..18c3acf1c 100644 --- a/superset/assets/package-lock.json +++ b/superset/assets/package-lock.json @@ -2232,9 +2232,9 @@ } }, "@superset-ui/color": { - "version": "0.10.1", - "resolved": "https://registry.npmjs.org/@superset-ui/color/-/color-0.10.1.tgz", - "integrity": "sha512-GblA9+h947un4K6s6v3uRTYCDEBi8GAp3wyEHVXfhSv/YXwyzpyhvhXoF8APSz+8cDVkKYY2svZVOALE0QDI1Q==", + "version": "0.10.8", + "resolved": "https://registry.npmjs.org/@superset-ui/color/-/color-0.10.8.tgz", + "integrity": "sha512-H1M8V9OKO3fCmOHQvW1rN9pRw2t/L1LKHvxzEj/Kccw+osckdmF8RtKEp7DaBuKMO6PF2Kq2FWNIiqNtin9whA==", "requires": { "@types/d3-scale": "^2.0.2", "d3-scale": "^2.1.2" diff --git a/superset/assets/spec/javascripts/dashboard/components/DashboardBuilder_spec.jsx b/superset/assets/spec/javascripts/dashboard/components/DashboardBuilder_spec.jsx index bf24644e1..16dc33dea 100644 --- a/superset/assets/spec/javascripts/dashboard/components/DashboardBuilder_spec.jsx +++ b/superset/assets/spec/javascripts/dashboard/components/DashboardBuilder_spec.jsx @@ -31,6 +31,7 @@ import DashboardComponent from '../../../../src/dashboard/containers/DashboardCo import DashboardHeader from '../../../../src/dashboard/containers/DashboardHeader'; import DashboardGrid from '../../../../src/dashboard/containers/DashboardGrid'; import * as dashboardStateActions from '../../../../src/dashboard/actions/dashboardState'; +import { BUILDER_PANE_TYPE } from '../../../../src/dashboard/util/constants'; import WithDragDropContext from '../helpers/WithDragDropContext'; import { @@ -61,7 +62,10 @@ describe('DashboardBuilder', () => { dashboardLayout, deleteTopLevelTabs() {}, editMode: false, - showBuilderPane: false, + showBuilderPane() {}, + builderPaneType: BUILDER_PANE_TYPE.NONE, + setColorSchemeAndUnsavedChanges() {}, + colorScheme: undefined, handleComponentDrop() {}, toggleBuilderPane() {}, }; @@ -143,11 +147,27 @@ describe('DashboardBuilder', () => { expect(parentSize.find(DashboardGrid)).toHaveLength(expectedCount); }); - it('should render a BuilderComponentPane if editMode=showBuilderPane=true', () => { + it('should render a BuilderComponentPane if editMode=true and user selects "Insert Components" pane', () => { const wrapper = setup(); expect(wrapper.find(BuilderComponentPane)).toHaveLength(0); - wrapper.setProps({ ...props, editMode: true, showBuilderPane: true }); + wrapper.setProps({ + ...props, + editMode: true, + builderPaneType: BUILDER_PANE_TYPE.ADD_COMPONENTS, + }); + expect(wrapper.find(BuilderComponentPane)).toHaveLength(1); + }); + + it('should render a BuilderComponentPane if editMode=true and user selects "Colors" pane', () => { + const wrapper = setup(); + expect(wrapper.find(BuilderComponentPane)).toHaveLength(0); + + wrapper.setProps({ + ...props, + editMode: true, + builderPaneType: BUILDER_PANE_TYPE.COLORS, + }); expect(wrapper.find(BuilderComponentPane)).toHaveLength(1); }); diff --git a/superset/assets/spec/javascripts/dashboard/components/Header_spec.jsx b/superset/assets/spec/javascripts/dashboard/components/Header_spec.jsx index 2fff4638c..69e57da8d 100644 --- a/superset/assets/spec/javascripts/dashboard/components/Header_spec.jsx +++ b/superset/assets/spec/javascripts/dashboard/components/Header_spec.jsx @@ -24,6 +24,7 @@ import FaveStar from '../../../../src/components/FaveStar'; import HeaderActionsDropdown from '../../../../src/dashboard/components/HeaderActionsDropdown'; import Button from '../../../../src/components/Button'; import UndoRedoKeylisteners from '../../../../src/dashboard/components/UndoRedoKeylisteners'; +import { BUILDER_PANE_TYPE } from '../../../../src/dashboard/util/constants'; describe('Header', () => { const props = { @@ -46,7 +47,8 @@ describe('Header', () => { updateDashboardTitle: () => {}, editMode: false, setEditMode: () => {}, - showBuilderPane: false, + showBuilderPane: () => {}, + builderPaneType: BUILDER_PANE_TYPE.NONE, toggleBuilderPane: () => {}, updateCss: () => {}, hasUnsavedChanges: false, @@ -150,9 +152,9 @@ describe('Header', () => { expect(wrapper.find(HeaderActionsDropdown)).toHaveLength(1); }); - it('should render four Buttons', () => { + it('should render five Buttons', () => { const wrapper = setup(overrideProps); - expect(wrapper.find(Button)).toHaveLength(4); + expect(wrapper.find(Button)).toHaveLength(5); }); it('should set up undo/redo', () => { diff --git a/superset/assets/spec/javascripts/dashboard/fixtures/mockDashboardState.js b/superset/assets/spec/javascripts/dashboard/fixtures/mockDashboardState.js index f326a76ee..3763ef41a 100644 --- a/superset/assets/spec/javascripts/dashboard/fixtures/mockDashboardState.js +++ b/superset/assets/spec/javascripts/dashboard/fixtures/mockDashboardState.js @@ -17,6 +17,7 @@ * under the License. */ import { id as sliceId } from './mockChartQueries'; +import { BUILDER_PANE_TYPE } from '../../../../src/dashboard/util/constants'; export default { sliceIds: [sliceId], @@ -24,7 +25,7 @@ export default { filters: {}, expandedSlices: {}, editMode: false, - showBuilderPane: false, + builderPaneType: BUILDER_PANE_TYPE.NONE, hasUnsavedChanges: false, maxUndoHistoryExceeded: false, isStarred: true, diff --git a/superset/assets/spec/javascripts/dashboard/reducers/dashboardState_spec.js b/superset/assets/spec/javascripts/dashboard/reducers/dashboardState_spec.js index c3e385580..dadcf06c8 100644 --- a/superset/assets/spec/javascripts/dashboard/reducers/dashboardState_spec.js +++ b/superset/assets/spec/javascripts/dashboard/reducers/dashboardState_spec.js @@ -25,12 +25,12 @@ import { SET_EDIT_MODE, SET_MAX_UNDO_HISTORY_EXCEEDED, SET_UNSAVED_CHANGES, - TOGGLE_BUILDER_PANE, TOGGLE_EXPAND_SLICE, TOGGLE_FAVE_STAR, } from '../../../../src/dashboard/actions/dashboardState'; import dashboardStateReducer from '../../../../src/dashboard/reducers/dashboardState'; +import { BUILDER_PANE_TYPE } from '../../../../src/dashboard/util/constants'; describe('dashboardState reducer', () => { it('should return initial state', () => { @@ -79,23 +79,10 @@ describe('dashboardState reducer', () => { { editMode: false }, { type: SET_EDIT_MODE, editMode: true }, ), - ).toEqual({ editMode: true, showBuilderPane: true }); - }); - - it('should toggle builder pane', () => { - expect( - dashboardStateReducer( - { showBuilderPane: false }, - { type: TOGGLE_BUILDER_PANE }, - ), - ).toEqual({ showBuilderPane: true }); - - expect( - dashboardStateReducer( - { showBuilderPane: true }, - { type: TOGGLE_BUILDER_PANE }, - ), - ).toEqual({ showBuilderPane: false }); + ).toEqual({ + editMode: true, + builderPaneType: BUILDER_PANE_TYPE.ADD_COMPONENTS, + }); }); it('should toggle expanded slices', () => { @@ -150,6 +137,8 @@ describe('dashboardState reducer', () => { hasUnsavedChanges: false, maxUndoHistoryExceeded: false, editMode: false, + builderPaneType: BUILDER_PANE_TYPE.NONE, + updatedColorScheme: false, }); }); diff --git a/superset/assets/src/chart/ChartRenderer.jsx b/superset/assets/src/chart/ChartRenderer.jsx index e0a01f14c..e46f941ff 100644 --- a/superset/assets/src/chart/ChartRenderer.jsx +++ b/superset/assets/src/chart/ChartRenderer.jsx @@ -87,7 +87,8 @@ class ChartRenderer extends React.Component { nextProps.height !== this.props.height || nextProps.width !== this.props.width || nextState.tooltip !== this.state.tooltip || - nextProps.triggerRender) { + nextProps.triggerRender || + nextProps.formData.color_scheme !== this.props.formData.color_scheme) { return true; } } diff --git a/superset/assets/src/dashboard/actions/dashboardLayout.js b/superset/assets/src/dashboard/actions/dashboardLayout.js index 5b163d961..2716b006e 100644 --- a/superset/assets/src/dashboard/actions/dashboardLayout.js +++ b/superset/assets/src/dashboard/actions/dashboardLayout.js @@ -209,7 +209,8 @@ export function undoLayoutAction() { if ( dashboardLayout.past.length === 0 && - !dashboardState.maxUndoHistoryExceeded + !dashboardState.maxUndoHistoryExceeded && + !dashboardState.updatedColorScheme ) { dispatch(setUnsavedChanges(false)); } diff --git a/superset/assets/src/dashboard/actions/dashboardState.js b/superset/assets/src/dashboard/actions/dashboardState.js index 086481919..5a036449b 100644 --- a/superset/assets/src/dashboard/actions/dashboardState.js +++ b/superset/assets/src/dashboard/actions/dashboardState.js @@ -225,9 +225,9 @@ export function startPeriodicRender(interval) { }; } -export const TOGGLE_BUILDER_PANE = 'TOGGLE_BUILDER_PANE'; -export function toggleBuilderPane() { - return { type: TOGGLE_BUILDER_PANE }; +export const SHOW_BUILDER_PANE = 'SHOW_BUILDER_PANE'; +export function showBuilderPane(builderPaneType) { + return { type: SHOW_BUILDER_PANE, builderPaneType }; } export function addSliceToDashboard(id) { @@ -266,6 +266,18 @@ export function removeSliceFromDashboard(id) { }; } +export const SET_COLOR_SCHEME = 'SET_COLOR_SCHEME'; +export function setColorScheme(colorScheme) { + return { type: SET_COLOR_SCHEME, colorScheme }; +} + +export function setColorSchemeAndUnsavedChanges(colorScheme) { + return dispatch => { + dispatch(setColorScheme(colorScheme)); + dispatch(setUnsavedChanges(true)); + }; +} + // Undo history --------------------------------------------------------------- export const SET_MAX_UNDO_HISTORY_EXCEEDED = 'SET_MAX_UNDO_HISTORY_EXCEEDED'; export function setMaxUndoHistoryExceeded(maxUndoHistoryExceeded = true) { diff --git a/superset/assets/src/dashboard/components/BuilderComponentPane.jsx b/superset/assets/src/dashboard/components/BuilderComponentPane.jsx index 4c2e92ce3..2d2ab0899 100644 --- a/superset/assets/src/dashboard/components/BuilderComponentPane.jsx +++ b/superset/assets/src/dashboard/components/BuilderComponentPane.jsx @@ -19,49 +19,37 @@ /* eslint-env browser */ import PropTypes from 'prop-types'; import React from 'react'; -import cx from 'classnames'; import { StickyContainer, Sticky } from 'react-sticky'; import { ParentSize } from '@vx/responsive'; -import { t } from '@superset-ui/translation'; -import NewColumn from './gridComponents/new/NewColumn'; -import NewDivider from './gridComponents/new/NewDivider'; -import NewHeader from './gridComponents/new/NewHeader'; -import NewRow from './gridComponents/new/NewRow'; -import NewTabs from './gridComponents/new/NewTabs'; -import NewMarkdown from './gridComponents/new/NewMarkdown'; -import SliceAdder from '../containers/SliceAdder'; - -const SUPERSET_HEADER_HEIGHT = 59; +import InsertComponentPane, { + SUPERSET_HEADER_HEIGHT, +} from './InsertComponentPane'; +import ColorComponentPane from './ColorComponentPane'; +import { BUILDER_PANE_TYPE } from '../util/constants'; const propTypes = { topOffset: PropTypes.number, - toggleBuilderPane: PropTypes.func.isRequired, + showBuilderPane: PropTypes.func.isRequired, + builderPaneType: PropTypes.string.isRequired, + setColorSchemeAndUnsavedChanges: PropTypes.func.isRequired, + colorScheme: PropTypes.string, }; const defaultProps = { topOffset: 0, + colorScheme: undefined, }; class BuilderComponentPane extends React.PureComponent { - constructor(props) { - super(props); - this.state = { - slideDirection: 'slide-out', - }; - - this.openSlicesPane = this.slide.bind(this, 'slide-in'); - this.closeSlicesPane = this.slide.bind(this, 'slide-out'); - } - - slide(direction) { - this.setState({ - slideDirection: direction, - }); - } - render() { - const { topOffset } = this.props; + const { + topOffset, + builderPaneType, + showBuilderPane, + setColorSchemeAndUnsavedChanges, + colorScheme, + } = this.props; return (
-
-
-
- {t('Insert components')} - -
-
-
-
- {t('Your charts & filters')} -
- - -
- - - - - - -
-
-
- - {t('Your charts and filters')} -
- -
-
+ {builderPaneType === BUILDER_PANE_TYPE.ADD_COMPONENTS && ( + + )} + {builderPaneType === BUILDER_PANE_TYPE.COLORS && ( + + )}
)} diff --git a/superset/assets/src/dashboard/components/ColorComponentPane.jsx b/superset/assets/src/dashboard/components/ColorComponentPane.jsx new file mode 100644 index 000000000..ee6aec585 --- /dev/null +++ b/superset/assets/src/dashboard/components/ColorComponentPane.jsx @@ -0,0 +1,107 @@ +/** + * 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. + */ +/* eslint-env browser */ +import PropTypes from 'prop-types'; +import React from 'react'; +import { getCategoricalSchemeRegistry } from '@superset-ui/color'; +import { t } from '@superset-ui/translation'; + +import ColorSchemeControl from '../../explore/components/controls/ColorSchemeControl'; +import { BUILDER_PANE_TYPE } from '../util/constants'; + +const propTypes = { + showBuilderPane: PropTypes.func.isRequired, + setColorSchemeAndUnsavedChanges: PropTypes.func.isRequired, + colorScheme: PropTypes.string, +}; + +const defaultProps = { + colorScheme: undefined, +}; + +class ColorComponentPane extends React.PureComponent { + constructor(props) { + super(props); + this.state = { hovered: false }; + this.categoricalSchemeRegistry = getCategoricalSchemeRegistry(); + this.getChoices = this.getChoices.bind(this); + this.getSchemes = this.getSchemes.bind(this); + this.onCloseButtonClick = this.onCloseButtonClick.bind(this); + this.onMouseEnter = this.setHover.bind(this, true); + this.onMouseLeave = this.setHover.bind(this, false); + } + + onCloseButtonClick() { + this.props.showBuilderPane(BUILDER_PANE_TYPE.NONE); + } + + getChoices() { + return this.categoricalSchemeRegistry.keys().map(s => [s, s]); + } + + getSchemes() { + return this.categoricalSchemeRegistry.getMap(); + } + + setHover(hovered) { + this.setState({ hovered }); + } + + render() { + const { setColorSchemeAndUnsavedChanges, colorScheme } = this.props; + + return ( +
+
+
+ {'Color Settings'} + +
+
+ +
+
+
+ ); + } +} + +ColorComponentPane.propTypes = propTypes; +ColorComponentPane.defaultProps = defaultProps; + +export default ColorComponentPane; diff --git a/superset/assets/src/dashboard/components/DashboardBuilder.jsx b/superset/assets/src/dashboard/components/DashboardBuilder.jsx index e635f902d..7bd6f6f39 100644 --- a/superset/assets/src/dashboard/components/DashboardBuilder.jsx +++ b/superset/assets/src/dashboard/components/DashboardBuilder.jsx @@ -38,6 +38,7 @@ import WithPopoverMenu from './menu/WithPopoverMenu'; import getDragDropManager from '../util/getDragDropManager'; import { + BUILDER_PANE_TYPE, DASHBOARD_GRID_ID, DASHBOARD_ROOT_ID, DASHBOARD_ROOT_DEPTH, @@ -51,13 +52,15 @@ const propTypes = { dashboardLayout: PropTypes.object.isRequired, deleteTopLevelTabs: PropTypes.func.isRequired, editMode: PropTypes.bool.isRequired, - showBuilderPane: PropTypes.bool, + showBuilderPane: PropTypes.func.isRequired, + builderPaneType: PropTypes.string.isRequired, + setColorSchemeAndUnsavedChanges: PropTypes.func.isRequired, + colorScheme: PropTypes.string, handleComponentDrop: PropTypes.func.isRequired, - toggleBuilderPane: PropTypes.func.isRequired, }; const defaultProps = { - showBuilderPane: false, + colorScheme: undefined, }; class DashboardBuilder extends React.Component { @@ -102,7 +105,15 @@ class DashboardBuilder extends React.Component { } render() { - const { handleComponentDrop, dashboardLayout, editMode } = this.props; + const { + handleComponentDrop, + dashboardLayout, + editMode, + showBuilderPane, + builderPaneType, + setColorSchemeAndUnsavedChanges, + colorScheme, + } = this.props; const { tabIndex } = this.state; const dashboardRoot = dashboardLayout[DASHBOARD_ROOT_ID]; const rootChildId = dashboardRoot.children[0]; @@ -202,10 +213,13 @@ class DashboardBuilder extends React.Component { )}
- {this.props.editMode && this.props.showBuilderPane && ( + {editMode && builderPaneType !== BUILDER_PANE_TYPE.NONE && ( )}
diff --git a/superset/assets/src/dashboard/components/Header.jsx b/superset/assets/src/dashboard/components/Header.jsx index 796a2df09..366efbe3d 100644 --- a/superset/assets/src/dashboard/components/Header.jsx +++ b/superset/assets/src/dashboard/components/Header.jsx @@ -19,6 +19,7 @@ /* eslint-env browser */ import React from 'react'; import PropTypes from 'prop-types'; +import { CategoricalColorNamespace } from '@superset-ui/color'; import { t } from '@superset-ui/translation'; import HeaderActionsDropdown from './HeaderActionsDropdown'; @@ -29,6 +30,7 @@ import UndoRedoKeylisteners from './UndoRedoKeylisteners'; import { chartPropShape } from '../util/propShapes'; import { + BUILDER_PANE_TYPE, UNDO_LIMIT, SAVE_TYPE_OVERWRITE, DASHBOARD_POSITION_DATA_LIMIT, @@ -52,6 +54,8 @@ const propTypes = { filters: PropTypes.object.isRequired, expandedSlices: PropTypes.object.isRequired, css: PropTypes.string.isRequired, + colorNamespace: PropTypes.string, + colorScheme: PropTypes.string, isStarred: PropTypes.bool.isRequired, isLoading: PropTypes.bool.isRequired, onSave: PropTypes.func.isRequired, @@ -63,8 +67,8 @@ const propTypes = { updateDashboardTitle: PropTypes.func.isRequired, editMode: PropTypes.bool.isRequired, setEditMode: PropTypes.func.isRequired, - showBuilderPane: PropTypes.bool.isRequired, - toggleBuilderPane: PropTypes.func.isRequired, + showBuilderPane: PropTypes.func.isRequired, + builderPaneType: PropTypes.string.isRequired, updateCss: PropTypes.func.isRequired, logEvent: PropTypes.func.isRequired, hasUnsavedChanges: PropTypes.bool.isRequired, @@ -81,6 +85,11 @@ const propTypes = { setRefreshFrequency: PropTypes.func.isRequired, }; +const defaultProps = { + colorNamespace: undefined, + colorScheme: undefined, +}; + class Header extends React.PureComponent { static discardChanges() { window.location.reload(); @@ -96,6 +105,10 @@ class Header extends React.PureComponent { this.handleChangeText = this.handleChangeText.bind(this); this.handleCtrlZ = this.handleCtrlZ.bind(this); this.handleCtrlY = this.handleCtrlY.bind(this); + this.onInsertComponentsButtonClick = this.onInsertComponentsButtonClick.bind( + this, + ); + this.onColorsButtonClick = this.onColorsButtonClick.bind(this); this.toggleEditMode = this.toggleEditMode.bind(this); this.forceRefresh = this.forceRefresh.bind(this); this.startPeriodicRender = this.startPeriodicRender.bind(this); @@ -128,25 +141,12 @@ class Header extends React.PureComponent { clearTimeout(this.ctrlZTimeout); } - forceRefresh() { - if (!this.props.isLoading) { - const chartList = Object.values(this.props.charts); - this.props.logEvent(LOG_ACTIONS_FORCE_REFRESH_DASHBOARD, { - force: true, - interval: 0, - chartCount: chartList.length, - }); - return this.props.fetchCharts(chartList, true); - } - return false; + onInsertComponentsButtonClick() { + this.props.showBuilderPane(BUILDER_PANE_TYPE.ADD_COMPONENTS); } - startPeriodicRender(interval) { - this.props.logEvent(LOG_ACTIONS_PERIODIC_RENDER_DASHBOARD, { - force: true, - interval, - }); - return this.props.startPeriodicRender(interval); + onColorsButtonClick() { + this.props.showBuilderPane(BUILDER_PANE_TYPE.COLORS); } handleChangeText(nextText) { @@ -177,6 +177,27 @@ class Header extends React.PureComponent { }); } + forceRefresh() { + if (!this.props.isLoading) { + const chartList = Object.values(this.props.charts); + this.props.logEvent(LOG_ACTIONS_FORCE_REFRESH_DASHBOARD, { + force: true, + interval: 0, + chartCount: chartList.length, + }); + return this.props.fetchCharts(chartList, true); + } + return false; + } + + startPeriodicRender(interval) { + this.props.logEvent(LOG_ACTIONS_PERIODIC_RENDER_DASHBOARD, { + force: true, + interval, + }); + return this.props.startPeriodicRender(interval); + } + toggleEditMode() { this.props.logEvent(LOG_ACTIONS_TOGGLE_EDIT_DASHBOARD, { edit_mode: !this.props.editMode, @@ -190,14 +211,24 @@ class Header extends React.PureComponent { layout: positions, expandedSlices, css, + colorNamespace, + colorScheme, filters, dashboardInfo, } = this.props; + const scale = CategoricalColorNamespace.getScale( + colorScheme, + colorNamespace, + ); + const labelColors = scale.getColorMap(); const data = { positions, expanded_slices: expandedSlices, css, + color_namespace: colorNamespace, + color_scheme: colorScheme, + label_colors: labelColors, dashboard_title: dashboardTitle, default_filters: safeStringify(filters), }; @@ -229,6 +260,8 @@ class Header extends React.PureComponent { filters, expandedSlices, css, + colorNamespace, + colorScheme, onUndo, onRedo, undoLength, @@ -237,7 +270,7 @@ class Header extends React.PureComponent { onSave, updateCss, editMode, - showBuilderPane, + builderPaneType, dashboardInfo, hasUnsavedChanges, isLoading, @@ -294,10 +327,22 @@ class Header extends React.PureComponent { )} {editMode && ( - + )} + + {editMode && ( + )} @@ -351,6 +396,8 @@ class Header extends React.PureComponent { filters={filters} expandedSlices={expandedSlices} css={css} + colorNamespace={colorNamespace} + colorScheme={colorScheme} onSave={onSave} onChange={onChange} forceRefreshAllCharts={this.forceRefresh} @@ -371,5 +418,6 @@ class Header extends React.PureComponent { } Header.propTypes = propTypes; +Header.defaultProps = defaultProps; export default Header; diff --git a/superset/assets/src/dashboard/components/HeaderActionsDropdown.jsx b/superset/assets/src/dashboard/components/HeaderActionsDropdown.jsx index 4c36d9a3c..5fc9d0ede 100644 --- a/superset/assets/src/dashboard/components/HeaderActionsDropdown.jsx +++ b/superset/assets/src/dashboard/components/HeaderActionsDropdown.jsx @@ -37,6 +37,8 @@ const propTypes = { dashboardTitle: PropTypes.string.isRequired, hasUnsavedChanges: PropTypes.bool.isRequired, css: PropTypes.string.isRequired, + colorNamespace: PropTypes.string, + colorScheme: PropTypes.string, onChange: PropTypes.func.isRequired, updateCss: PropTypes.func.isRequired, forceRefreshAllCharts: PropTypes.func.isRequired, @@ -53,7 +55,10 @@ const propTypes = { onSave: PropTypes.func.isRequired, }; -const defaultProps = {}; +const defaultProps = { + colorNamespace: undefined, + colorScheme: undefined, +}; class HeaderActionsDropdown extends React.PureComponent { static discardChanges() { @@ -111,6 +116,8 @@ class HeaderActionsDropdown extends React.PureComponent { refreshFrequency, editMode, css, + colorNamespace, + colorScheme, hasUnsavedChanges, layout, filters, @@ -145,6 +152,8 @@ class HeaderActionsDropdown extends React.PureComponent { expandedSlices={expandedSlices} refreshFrequency={refreshFrequency} css={css} + colorNamespace={colorNamespace} + colorScheme={colorScheme} onSave={onSave} isMenuItem triggerNode={{t('Save as')}} diff --git a/superset/assets/src/dashboard/components/InsertComponentPane.jsx b/superset/assets/src/dashboard/components/InsertComponentPane.jsx new file mode 100644 index 000000000..31413471f --- /dev/null +++ b/superset/assets/src/dashboard/components/InsertComponentPane.jsx @@ -0,0 +1,118 @@ +/** + * 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. + */ +/* eslint-env browser */ +import PropTypes from 'prop-types'; +import React from 'react'; +import cx from 'classnames'; +import { t } from '@superset-ui/translation'; + +import NewColumn from './gridComponents/new/NewColumn'; +import NewDivider from './gridComponents/new/NewDivider'; +import NewHeader from './gridComponents/new/NewHeader'; +import NewRow from './gridComponents/new/NewRow'; +import NewTabs from './gridComponents/new/NewTabs'; +import NewMarkdown from './gridComponents/new/NewMarkdown'; +import SliceAdder from '../containers/SliceAdder'; +import { BUILDER_PANE_TYPE } from '../util/constants'; + +export const SUPERSET_HEADER_HEIGHT = 59; + +const propTypes = { + height: PropTypes.number.isRequired, + isSticky: PropTypes.bool.isRequired, + showBuilderPane: PropTypes.func.isRequired, +}; + +class InsertComponentPane extends React.PureComponent { + constructor(props) { + super(props); + this.state = { + slideDirection: 'slide-out', + }; + + this.onCloseButtonClick = this.onCloseButtonClick.bind(this); + this.openSlicesPane = this.slide.bind(this, 'slide-in'); + this.closeSlicesPane = this.slide.bind(this, 'slide-out'); + } + + onCloseButtonClick() { + this.props.showBuilderPane(BUILDER_PANE_TYPE.NONE); + } + + slide(direction) { + this.setState({ + slideDirection: direction, + }); + } + + render() { + return ( +
+
+
+ {t('Insert components')} + +
+
+
+
+ {t('Your charts & filters')} +
+ + +
+ + + + + + +
+
+
+ + {t('Your charts and filters')} +
+ +
+
+ ); + } +} + +InsertComponentPane.propTypes = propTypes; + +export default InsertComponentPane; diff --git a/superset/assets/src/dashboard/components/SaveModal.jsx b/superset/assets/src/dashboard/components/SaveModal.jsx index aa5436979..1873f0c31 100644 --- a/superset/assets/src/dashboard/components/SaveModal.jsx +++ b/superset/assets/src/dashboard/components/SaveModal.jsx @@ -20,6 +20,7 @@ import React from 'react'; import PropTypes from 'prop-types'; import { Button, FormControl, FormGroup, Radio } from 'react-bootstrap'; +import { CategoricalColorNamespace } from '@superset-ui/color'; import { t } from '@superset-ui/translation'; import ModalTrigger from '../../components/ModalTrigger'; @@ -38,6 +39,8 @@ const propTypes = { triggerNode: PropTypes.node.isRequired, filters: PropTypes.object.isRequired, css: PropTypes.string.isRequired, + colorNamespace: PropTypes.string, + colorScheme: PropTypes.string, onSave: PropTypes.func.isRequired, isMenuItem: PropTypes.bool, canOverwrite: PropTypes.bool.isRequired, @@ -47,6 +50,8 @@ const propTypes = { const defaultProps = { isMenuItem: false, saveType: SAVE_TYPE_OVERWRITE, + colorNamespace: undefined, + colorScheme: undefined, }; class SaveModal extends React.PureComponent { @@ -93,15 +98,25 @@ class SaveModal extends React.PureComponent { dashboardTitle, layout: positions, css, + colorNamespace, + colorScheme, expandedSlices, filters, dashboardId, refreshFrequency, } = this.props; + const scale = CategoricalColorNamespace.getScale( + colorScheme, + colorNamespace, + ); + const labelColors = scale.getColorMap(); const data = { positions, css, + color_namespace: colorNamespace, + color_scheme: colorScheme, + label_colors: labelColors, expanded_slices: expandedSlices, dashboard_title: saveType === SAVE_TYPE_NEWDASHBOARD ? newDashName : dashboardTitle, diff --git a/superset/assets/src/dashboard/containers/Chart.jsx b/superset/assets/src/dashboard/containers/Chart.jsx index 1e0e64c60..c0926db0a 100644 --- a/superset/assets/src/dashboard/containers/Chart.jsx +++ b/superset/assets/src/dashboard/containers/Chart.jsx @@ -43,7 +43,7 @@ function mapStateToProps( ) { const { id } = ownProps; const chart = chartQueries[id] || {}; - const { filters } = dashboardState; + const { filters, colorScheme } = dashboardState; return { chart, @@ -58,6 +58,7 @@ function mapStateToProps( chart, dashboardMetadata: dashboardInfo.metadata, filters, + colorScheme, sliceId: id, }), editMode: dashboardState.editMode, diff --git a/superset/assets/src/dashboard/containers/DashboardBuilder.jsx b/superset/assets/src/dashboard/containers/DashboardBuilder.jsx index 3ca70431a..3c6514a2f 100644 --- a/superset/assets/src/dashboard/containers/DashboardBuilder.jsx +++ b/superset/assets/src/dashboard/containers/DashboardBuilder.jsx @@ -20,7 +20,10 @@ import { bindActionCreators } from 'redux'; import { connect } from 'react-redux'; import DashboardBuilder from '../components/DashboardBuilder'; -import { toggleBuilderPane } from '../actions/dashboardState'; +import { + setColorSchemeAndUnsavedChanges, + showBuilderPane, +} from '../actions/dashboardState'; import { deleteTopLevelTabs, handleComponentDrop, @@ -30,7 +33,8 @@ function mapStateToProps({ dashboardLayout: undoableLayout, dashboardState }) { return { dashboardLayout: undoableLayout.present, editMode: dashboardState.editMode, - showBuilderPane: dashboardState.showBuilderPane, + builderPaneType: dashboardState.builderPaneType, + colorScheme: dashboardState.colorScheme, }; } @@ -39,7 +43,8 @@ function mapDispatchToProps(dispatch) { { deleteTopLevelTabs, handleComponentDrop, - toggleBuilderPane, + showBuilderPane, + setColorSchemeAndUnsavedChanges, }, dispatch, ); diff --git a/superset/assets/src/dashboard/containers/DashboardHeader.jsx b/superset/assets/src/dashboard/containers/DashboardHeader.jsx index 570d790ae..05e90fb92 100644 --- a/superset/assets/src/dashboard/containers/DashboardHeader.jsx +++ b/superset/assets/src/dashboard/containers/DashboardHeader.jsx @@ -24,7 +24,7 @@ import isDashboardLoading from '../util/isDashboardLoading'; import { setEditMode, - toggleBuilderPane, + showBuilderPane, fetchFaveStar, saveFaveStar, fetchCharts, @@ -71,6 +71,8 @@ function mapStateToProps({ expandedSlices: dashboardState.expandedSlices, refreshFrequency: dashboardState.refreshFrequency, css: dashboardState.css, + colorNamespace: dashboardState.colorNamespace, + colorScheme: dashboardState.colorScheme, charts, userId: dashboardInfo.userId, isStarred: !!dashboardState.isStarred, @@ -78,7 +80,7 @@ function mapStateToProps({ hasUnsavedChanges: !!dashboardState.hasUnsavedChanges, maxUndoHistoryExceeded: !!dashboardState.maxUndoHistoryExceeded, editMode: !!dashboardState.editMode, - showBuilderPane: !!dashboardState.showBuilderPane, + builderPaneType: dashboardState.builderPaneType, }; } @@ -91,7 +93,7 @@ function mapDispatchToProps(dispatch) { onUndo: undoLayoutAction, onRedo: redoLayoutAction, setEditMode, - toggleBuilderPane, + showBuilderPane, fetchFaveStar, saveFaveStar, fetchCharts, diff --git a/superset/assets/src/dashboard/reducers/dashboardState.js b/superset/assets/src/dashboard/reducers/dashboardState.js index 24066ebca..007f63a28 100644 --- a/superset/assets/src/dashboard/reducers/dashboardState.js +++ b/superset/assets/src/dashboard/reducers/dashboardState.js @@ -23,15 +23,17 @@ import { ON_CHANGE, ON_SAVE, REMOVE_SLICE, + SET_COLOR_SCHEME, SET_EDIT_MODE, SET_MAX_UNDO_HISTORY_EXCEEDED, SET_UNSAVED_CHANGES, - TOGGLE_BUILDER_PANE, + SHOW_BUILDER_PANE, TOGGLE_EXPAND_SLICE, TOGGLE_FAVE_STAR, UPDATE_CSS, SET_REFRESH_FREQUENCY, } from '../actions/dashboardState'; +import { BUILDER_PANE_TYPE } from '../util/constants'; export default function dashboardStateReducer(state = {}, action) { const actionHandlers = { @@ -73,15 +75,24 @@ export default function dashboardStateReducer(state = {}, action) { return { ...state, editMode: action.editMode, - showBuilderPane: !!action.editMode, + builderPaneType: action.editMode + ? BUILDER_PANE_TYPE.ADD_COMPONENTS + : BUILDER_PANE_TYPE.NONE, }; }, [SET_MAX_UNDO_HISTORY_EXCEEDED]() { const { maxUndoHistoryExceeded = true } = action.payload; return { ...state, maxUndoHistoryExceeded }; }, - [TOGGLE_BUILDER_PANE]() { - return { ...state, showBuilderPane: !state.showBuilderPane }; + [SHOW_BUILDER_PANE]() { + return { ...state, builderPaneType: action.builderPaneType }; + }, + [SET_COLOR_SCHEME]() { + return { + ...state, + colorScheme: action.colorScheme, + updatedColorScheme: true, + }; }, [TOGGLE_EXPAND_SLICE]() { const updatedExpandedSlices = { ...state.expandedSlices }; @@ -102,6 +113,8 @@ export default function dashboardStateReducer(state = {}, action) { hasUnsavedChanges: false, maxUndoHistoryExceeded: false, editMode: false, + builderPaneType: BUILDER_PANE_TYPE.NONE, + updatedColorScheme: false, }; }, diff --git a/superset/assets/src/dashboard/reducers/getInitialState.js b/superset/assets/src/dashboard/reducers/getInitialState.js index a9c4d8f95..69396de78 100644 --- a/superset/assets/src/dashboard/reducers/getInitialState.js +++ b/superset/assets/src/dashboard/reducers/getInitialState.js @@ -17,6 +17,7 @@ * under the License. */ /* eslint-disable camelcase */ +import { isString } from 'lodash'; import shortid from 'shortid'; import { CategoricalColorNamespace } from '@superset-ui/color'; @@ -28,6 +29,7 @@ import findFirstParentContainerId from '../util/findFirstParentContainer'; import getEmptyLayout from '../util/getEmptyLayout'; import newComponentFactory from '../util/newComponentFactory'; import { + BUILDER_PANE_TYPE, DASHBOARD_HEADER_ID, GRID_DEFAULT_CHART_WIDTH, GRID_COLUMN_COUNT, @@ -55,9 +57,16 @@ export default function(bootstrapData) { // Priming the color palette with user's label-color mapping provided in // the dashboard's JSON metadata if (dashboard.metadata && dashboard.metadata.label_colors) { - const colorMap = dashboard.metadata.label_colors; + const scheme = dashboard.metadata.color_scheme; + const namespace = dashboard.metadata.color_namespace; + const colorMap = isString(dashboard.metadata.label_colors) + ? JSON.parse(dashboard.metadata.label_colors) + : dashboard.metadata.label_colors; Object.keys(colorMap).forEach(label => { - CategoricalColorNamespace.getScale().setColor(label, colorMap[label]); + CategoricalColorNamespace.getScale(scheme, namespace).setColor( + label, + colorMap[label], + ); }); } @@ -188,8 +197,13 @@ export default function(bootstrapData) { expandedSlices: dashboard.metadata.expanded_slices || {}, refreshFrequency: dashboard.metadata.refresh_frequency || 0, css: dashboard.css || '', + colorNamespace: dashboard.metadata.color_namespace, + colorScheme: dashboard.metadata.color_scheme, editMode: dashboard.dash_edit_perm && editMode, - showBuilderPane: dashboard.dash_edit_perm && editMode, + builderPaneType: + dashboard.dash_edit_perm && editMode + ? BUILDER_PANE_TYPE.ADD_COMPONENTS + : BUILDER_PANE_TYPE.NONE, hasUnsavedChanges: false, maxUndoHistoryExceeded: false, }, diff --git a/superset/assets/src/dashboard/stylesheets/builder-sidepane.less b/superset/assets/src/dashboard/stylesheets/builder-sidepane.less index 3b850c84f..6bf6f6faf 100644 --- a/superset/assets/src/dashboard/stylesheets/builder-sidepane.less +++ b/superset/assets/src/dashboard/stylesheets/builder-sidepane.less @@ -185,4 +185,18 @@ outline: none; } } + + .color-scheme-container { + list-style: none; + margin: 0; + padding: 0; + display: flex; + align-items: center; + } + + .color-scheme-container li { + flex-basis: 9px; + height: 10px; + margin: 9px 1px; + } } diff --git a/superset/assets/src/dashboard/stylesheets/dashboard.less b/superset/assets/src/dashboard/stylesheets/dashboard.less index 3dc9672b1..16541db1e 100644 --- a/superset/assets/src/dashboard/stylesheets/dashboard.less +++ b/superset/assets/src/dashboard/stylesheets/dashboard.less @@ -120,7 +120,14 @@ body { display: flex; flex-direction: row; flex-wrap: nowrap; - & > :not(:last-child) { + & > :nth-child(3) { + border-radius: 2px 0px 0px 2px; + border-right: none; + } + & > :nth-child(4) { + border-radius: 0px 2px 2px 0px; + } + & > :not(:nth-child(3)):not(:last-child) { margin-right: 8px; } } diff --git a/superset/assets/src/dashboard/util/charts/getFormDataWithExtraFilters.js b/superset/assets/src/dashboard/util/charts/getFormDataWithExtraFilters.js index f397a937f..a928a12b9 100644 --- a/superset/assets/src/dashboard/util/charts/getFormDataWithExtraFilters.js +++ b/superset/assets/src/dashboard/util/charts/getFormDataWithExtraFilters.js @@ -28,12 +28,15 @@ export default function getFormDataWithExtraFilters({ chart = {}, dashboardMetadata, filters, + colorScheme, sliceId, }) { // if dashboard metadata + filters have not changed, use cache if possible if ( (cachedDashboardMetadataByChart[sliceId] || {}) === dashboardMetadata && (cachedFiltersByChart[sliceId] || {}) === filters && + (colorScheme == null || + cachedFormdataByChart[sliceId].color_scheme === colorScheme) && !!cachedFormdataByChart[sliceId] ) { return cachedFormdataByChart[sliceId]; @@ -41,6 +44,7 @@ export default function getFormDataWithExtraFilters({ const formData = { ...chart.formData, + ...(colorScheme && { color_scheme: colorScheme }), extra_filters: getEffectiveExtraFilters({ dashboardMetadata, filters, diff --git a/superset/assets/src/dashboard/util/constants.js b/superset/assets/src/dashboard/util/constants.js index 5cce3ae37..9b33ca899 100644 --- a/superset/assets/src/dashboard/util/constants.js +++ b/superset/assets/src/dashboard/util/constants.js @@ -62,3 +62,10 @@ export const SAVE_TYPE_NEWDASHBOARD = 'newDashboard'; // default dashboard layout data size limit // could be overwritten by server-side config export const DASHBOARD_POSITION_DATA_LIMIT = 65535; + +// Dashboard pane types +export const BUILDER_PANE_TYPE = { + NONE: 'NONE', + ADD_COMPONENTS: 'ADD_COMPONENTS', + COLORS: 'COLORS', +}; diff --git a/superset/assets/src/dashboard/util/propShapes.jsx b/superset/assets/src/dashboard/util/propShapes.jsx index c433de924..c50ffc6a0 100644 --- a/superset/assets/src/dashboard/util/propShapes.jsx +++ b/superset/assets/src/dashboard/util/propShapes.jsx @@ -72,7 +72,10 @@ export const dashboardStatePropShape = PropTypes.shape({ filters: PropTypes.object.isRequired, expandedSlices: PropTypes.object, editMode: PropTypes.bool, - showBuilderPane: PropTypes.bool, + builderPaneType: PropTypes.string.isRequired, + colorNamespace: PropTypes.string, + colorScheme: PropTypes.string, + updatedColorScheme: PropTypes.bool, hasUnsavedChanges: PropTypes.bool, }); diff --git a/superset/assets/src/explore/components/controls/ColorSchemeControl.jsx b/superset/assets/src/explore/components/controls/ColorSchemeControl.jsx index 1e1e67718..34a4d3cf1 100644 --- a/superset/assets/src/explore/components/controls/ColorSchemeControl.jsx +++ b/superset/assets/src/explore/components/controls/ColorSchemeControl.jsx @@ -21,6 +21,7 @@ import PropTypes from 'prop-types'; import { isFunction } from 'lodash'; import { Creatable } from 'react-select'; import ControlHeader from '../ControlHeader'; +import TooltipWrapper from '../../../components/TooltipWrapper'; const propTypes = { description: PropTypes.string, @@ -77,17 +78,22 @@ export default class ColorSchemeControl extends React.PureComponent { } return ( -
    - {colors.map((color, i) => ( -
  •  
  • - ))} -
+ +
    + {colors.map((color, i) => ( +
  •  
  • + ))} +
+
); } diff --git a/superset/views/core.py b/superset/views/core.py index 39343d67c..794bcb36c 100755 --- a/superset/views/core.py +++ b/superset/views/core.py @@ -1741,6 +1741,12 @@ class Superset(BaseSupersetView): {key: v for key, v in default_filters_data.items() if int(key) in slice_ids} md['default_filters'] = json.dumps(applicable_filters) + if data.get('color_namespace'): + md['color_namespace'] = data.get('color_namespace') + if data.get('color_scheme'): + md['color_scheme'] = data.get('color_scheme') + if data.get('label_colors'): + md['label_colors'] = data.get('label_colors') dashboard.json_metadata = json.dumps(md) @api diff --git a/tests/dashboard_tests.py b/tests/dashboard_tests.py index c436753dd..04dcd5907 100644 --- a/tests/dashboard_tests.py +++ b/tests/dashboard_tests.py @@ -191,17 +191,60 @@ class DashboardTests(SupersetTestCase): data['dashboard_title'] = origin_title self.get_resp(url, data=dict(data=json.dumps(data))) + def test_save_dash_with_colors(self, username='admin'): + self.login(username=username) + dash = ( + db.session.query(models.Dashboard) + .filter_by(slug='births') + .first() + ) + positions = self.get_mock_positions(dash) + new_label_colors = { + 'data value': 'random color', + } + data = { + 'css': '', + 'expanded_slices': {}, + 'positions': positions, + 'dashboard_title': dash.dashboard_title, + 'color_namespace': 'Color Namespace Test', + 'color_scheme': 'Color Scheme Test', + 'label_colors': new_label_colors, + + } + url = '/superset/save_dash/{}/'.format(dash.id) + self.get_resp(url, data=dict(data=json.dumps(data))) + updatedDash = ( + db.session.query(models.Dashboard) + .filter_by(slug='births') + .first() + ) + self.assertIn('color_namespace', updatedDash.json_metadata) + self.assertIn('color_scheme', updatedDash.json_metadata) + self.assertIn('label_colors', updatedDash.json_metadata) + # bring back original dashboard + del data['color_namespace'] + del data['color_scheme'] + del data['label_colors'] + self.get_resp(url, data=dict(data=json.dumps(data))) + def test_copy_dash(self, username='admin'): self.login(username=username) dash = db.session.query(models.Dashboard).filter_by( slug='births').first() positions = self.get_mock_positions(dash) + new_label_colors = { + 'data value': 'random color', + } data = { 'css': '', 'duplicate_slices': False, 'expanded_slices': {}, 'positions': positions, 'dashboard_title': 'Copy Of Births', + 'color_namespace': 'Color Namespace Test', + 'color_scheme': 'Color Scheme Test', + 'label_colors': new_label_colors, } # Save changes to Births dashboard and retrieve updated dash From 82cf55375d8ec127b7160eb8b2668162ec96dd75 Mon Sep 17 00:00:00 2001 From: Beto Dealmeida Date: Thu, 28 Mar 2019 16:49:29 -0700 Subject: [PATCH 06/38] Revert PR #6933 (#7162) --- superset/views/core.py | 34 +++++----------------------------- 1 file changed, 5 insertions(+), 29 deletions(-) diff --git a/superset/views/core.py b/superset/views/core.py index 794bcb36c..3fa3771df 100755 --- a/superset/views/core.py +++ b/superset/views/core.py @@ -27,7 +27,7 @@ from urllib import parse from flask import ( abort, flash, g, Markup, redirect, render_template, request, Response, url_for, ) -from flask_appbuilder import expose, Model, SimpleFormView +from flask_appbuilder import expose, SimpleFormView from flask_appbuilder.actions import action from flask_appbuilder.models.sqla.interface import SQLAInterface from flask_appbuilder.security.decorators import has_access, has_access_api @@ -36,8 +36,7 @@ from flask_babel import lazy_gettext as _ import pandas as pd import simplejson as json import sqlalchemy as sqla -from sqlalchemy import ( - and_, Column, create_engine, ForeignKey, Integer, MetaData, or_, Table, update) +from sqlalchemy import and_, create_engine, MetaData, or_, update from sqlalchemy.engine.url import make_url from sqlalchemy.exc import IntegrityError from werkzeug.routing import BaseConverter @@ -101,14 +100,6 @@ def is_owner(obj, user): return obj and user in obj.owners -SQLTable = Table( - 'tables', - Model.metadata, # pylint: disable=no-member - Column('id', Integer, primary_key=True), - Column('database_id', Integer, ForeignKey('dbs.id')), - extend_existing=True) - - class DatabaseFilter(SupersetFilter): def apply(self, query, func): # noqa if security_manager.all_database_access(): @@ -121,19 +112,9 @@ class SliceFilter(SupersetFilter): def apply(self, query, func): # noqa if security_manager.all_datasource_access(): return query - + perms = self.get_view_menus('datasource_access') # TODO(bogdan): add `schema_access` support here - datasource_perms = self.get_view_menus('datasource_access') - database_perms = self.get_view_menus('database_access') - query = ( - query.outerjoin(SQLTable, self.model.datasource_id == SQLTable.c.id) - .outerjoin(models.Database, models.Database.id == SQLTable.c.database_id) - .filter(or_( - models.Database.perm.in_(database_perms), - self.model.perm.in_(datasource_perms), - )) - ) - return query + return query.filter(self.model.perm.in_(perms)) class DashboardFilter(SupersetFilter): @@ -151,12 +132,7 @@ class DashboardFilter(SupersetFilter): slice_ids_qry = ( db.session .query(Slice.id) - .outerjoin(SQLTable, Slice.datasource_id == SQLTable.c.id) - .outerjoin(models.Database, models.Database.id == SQLTable.c.database_id) - .filter(or_( - models.Database.perm.in_(datasource_perms), - Slice.perm.in_(datasource_perms), - )) + .filter(Slice.perm.in_(datasource_perms)) ) owner_ids_qry = ( db.session From ca6a73b028a1954c8e8d1dfa077bc7ee0a2694c0 Mon Sep 17 00:00:00 2001 From: Beto Dealmeida Date: Mon, 1 Apr 2019 21:06:01 -0700 Subject: [PATCH 07/38] Fix race condition when fetching results in SQL Lab (#7198) * Fix race condition when fetching results in SQL Lab * Fix lint --- superset/sql_lab.py | 9 ++------- superset/views/core.py | 2 +- 2 files changed, 3 insertions(+), 8 deletions(-) diff --git a/superset/sql_lab.py b/superset/sql_lab.py index a02a758a5..6a5ee8ebc 100644 --- a/superset/sql_lab.py +++ b/superset/sql_lab.py @@ -141,9 +141,7 @@ def get_sql_results( return handle_query_error(str(e), query, session) -def execute_sql_statement( - sql_statement, query, user_name, session, - cursor, return_results=False): +def execute_sql_statement(sql_statement, query, user_name, session, cursor): """Executes a single SQL statement""" database = query.database db_engine_spec = database.db_engine_spec @@ -256,11 +254,9 @@ def execute_sql_statements( logging.info(msg) query.set_extra_json_key('progress', msg) session.commit() - is_last_statement = i == len(statements) - 1 try: cdf = execute_sql_statement( - statement, query, user_name, session, cursor, - return_results=is_last_statement and return_results) + statement, query, user_name, session, cursor) msg = f'Running statement {i+1} out of {statement_count}' except Exception as e: msg = str(e) @@ -282,7 +278,6 @@ def execute_sql_statements( show_cols=False, latest_partition=False) query.end_time = now_as_float() - session.commit() payload.update({ 'status': query.status, diff --git a/superset/views/core.py b/superset/views/core.py index 3fa3771df..ad75c3b88 100755 --- a/superset/views/core.py +++ b/superset/views/core.py @@ -2544,7 +2544,7 @@ class Superset(BaseSupersetView): database_id=int(database_id), sql=sql, schema=schema, - select_as_cta=request.form.get('select_as_cta') == 'true', + select_as_cta=select_as_cta, start_time=now_as_float(), tab_name=request.form.get('tab'), status=QueryStatus.PENDING if async_ else QueryStatus.RUNNING, From 62cfb2d2e38e4aef230900485ec2cb9a01dd413d Mon Sep 17 00:00:00 2001 From: Kim Truong <47833996+khtruong@users.noreply.github.com> Date: Mon, 1 Apr 2019 21:39:20 -0700 Subject: [PATCH 08/38] Change number format default (#7137) * Sparkline dates aren't formatting in Time Series Table (#6976) * Exclude venv for python linter to ignore * Fix NaN error * Fix the white background shown in SQL editor on drag (#7021) This PR sets the background-color css property on `.ace_scroller` instead of `.ace_content` to prevent the white background shown during resizing of the SQL editor before drag ends. * Show tooltip with time frame (#6979) * Fix time filter control (#6978) * Enhancement of query context and object. (#6962) * added more functionalities for query context and object. * fixed cache logic * added default value for groupby * updated comments and removed print (cherry picked from commit d5b9795f87f79fa2c41e144ffc00fd9586be7657) * [fix] /superset/slice/id url is too long (#6989) (cherry picked from commit 6a4d507ab607b01ed324cb3341b71c6fb2cb5c97) * [WIP] fix user specified JSON metadata not updating dashboard on refresh (#7027) (cherry picked from commit cc58f0e661044e95c7c86d0da8d77a0a6640efe7) * feat: add ability to change font size in big number (#7003) * Add ability to change font sizes in Big Number * rename big number to header * Add comment to clarify font size values * Allow LIMIT to be specified in parameters (#7052) * [fix] Cursor jumping when editing chart and dashboard titles (#7038) (cherry picked from commit fc1770f7b79a4d8815b646b46390fabf190c3815) * Changing time table viz to pass formatTime a date (#7020) (cherry picked from commit 7f3c145b1f5a4e2d8b95982119503e98772e2c47) * [db-engine-spec] Aligning Hive/Presto partition logic (#7007) (cherry picked from commit 05be86611785fef2904992e4e7d31dce23f1c51b) * [fix] explore chart from dashboard missed slice title (#7046) (cherry picked from commit a6d48d4052839286aec725d51303b3b2bf6e8dd4) * fix inaccurate data calculation with adata rolling and contribution (#7035) (cherry picked from commit 0782e831cd37f665a2838119d87c433269f1b36b) * Adding warning message for sqllab save query (#7028) (cherry picked from commit ead3d48133e7e1ab8b91d51e561544a544b4eaad) * [datasource] Ensuring consistent behavior of datasource editing/saving. (#7037) * Update datasource.py * Update datasource.py (cherry picked from commit c771625f1068d3a7f41e6bced14b0cbdbf9962cc) * [csv-upload] Fixing message encoding (#6971) (cherry picked from commit 48431ab5b9375a94c5262a0336d9c69e5f01a3ac) * [sql-parse] Fixing LIMIT exceptions (#6963) (cherry picked from commit 3e076cb60b385e675ed1c9a8053493375e43370b) * Adding custom control overrides (#6956) * Adding extraOverrides to line chart * Updating extraOverrides to fit with more cases * Moving extraOverrides to index.js * Removing webpack-merge in package.json * Fixing metrics control clearing metric (cherry picked from commit e6194051f486e42922dc4e34a861f4490c1062fc) * [sqlparse] Fixing table name extraction for ill-defined query (#7029) (cherry picked from commit 07c340cf8203f13222f16efad1e55e202deb1865) * [missing values] Removing replacing missing values (#4905) (cherry picked from commit 61add606ca16a6ba981ccde864b121f5464b697a) * [SQL Lab] Improved query and results tabs rendering reliability (#7082) closes #7080 (cherry picked from commit 9b58e9f4920ef424e5b545dcbb4726e22bed5982) * Fix filter_box migration PR #6523 (#7066) * Fix filter_box migration PR #6523 * Fix druid-related bug (cherry picked from commit b210742ad24d01ca05bc58ca3342c90e301fe073) * SQL editor layout makeover (#7102) This PR includes the following layout and css tweaks: - Using flex to layout the north and south sub panes of query pane so resizing works properly in both Chrome and Firefox - Removal of necessary wrapper divs and tweaking of css in sql lab so we can scroll to the bottom of both the table list and the results pane - Make sql lab's content not overflow vertically and layout the query result area to eliminate double scroll bars - css tweaks on the basic.html page so the loading animation appears in the center of the page across the board (cherry picked from commit 71f1bbd2ec59b99d6ba6d9a4a2f9cfceaf922b80) * [forms] Fix handling of NULLs (cherry picked from commit e83a07d3dfda350cc44041cb6cbaec4510887902) * handle null column_name in sqla and druid models (cherry picked from commit 2ff721ae072b8d69c5cabddc3e1a388a596b1b6f) * Use metric name instead of metric in filter box (#7106) (cherry picked from commit 003364e74ea70cad1a4a6e784933fe8bef4c78ec) * Bump python lib croniter to an existing version (#7132) Package maintainers should really never delete packages, but it appears this happened with croniter and resulted in breaking our builds. This PR bumps to a more recent existing version of the library (cherry picked from commit 215ed392a11598eac228f57341dbfd232cf770e3) * Revert PR #6933 (#7162) * [bugfix] SQL Lab 'Filter Results' doesn't stick (#7104) When using a "Search Results" criteria, the subset of rows that match the criteria get displayed. While this the filter is applied, if another query is run, the filter is still active, but not displayed in the input text box. After this change, the state of the input box sticks after subsequent queries. (cherry picked from commit d5e8d663dc63ee9b99b3e7bb4e91512fa32ed1e0) * Injectable statsd client (#7138) * Add ability to inject statsd client; some py test/reqs updates - Updated the metrics logger to allow construction with an existing statsd client, so that it can be configured by external systems or libs. - added requirements to requirements-dev.txt which are needed to run tests-eg coverage, nose - removed dependency on mock lib, it is in python stdlib now - updated tox.ini to remove the now-superfluous deps * add license to test file, and remove blank line at EOF (cherry picked from commit ba19a62412aba27d9c38ec7013f13a976cb6c054) * [Lyft-GA] Enable color consistency in a dashboard (#7135) * Enable color consistency in a dashboard Moved actions, minor UI, allowed dashboard copy Fix linting errors Undo unintentional change Updated and added unit tests Fail quietly if package has not been updated Fail quietly on dashboard copy if package is old * Update packages * Remove unnecessary code * Addressed Grace's comments * Small fix for item key * Reset chart's color during exploration * Do not reset chart form data when exploring chart * Fix double scroll bars when content of sql result table overflows horizontally (#7168) The PR substracts the scrollbar height from the height of the container of the react virtualized table so we don't see double scrollbars. (cherry picked from commit 7ffcabd5c08bbe6aa022e66590c0ce805ae13f08) * Change number format default * Use smart formatter instead * fix merge issues * Use SMART_NUMBER --- superset/assets/backendSync.json | 8 ++++---- superset/assets/src/explore/controlPanels/Bubble.js | 3 --- superset/assets/src/explore/controls.jsx | 9 +++++---- 3 files changed, 9 insertions(+), 11 deletions(-) diff --git a/superset/assets/backendSync.json b/superset/assets/backendSync.json index 389da9cc2..68684bdcb 100644 --- a/superset/assets/backendSync.json +++ b/superset/assets/backendSync.json @@ -1635,7 +1635,7 @@ "freeForm": true, "label": "Number format", "renderTrigger": true, - "default": ".3s", + "default": "SMART_NUMBER", "choices": [ [ ".1s", @@ -2085,7 +2085,7 @@ "freeForm": true, "label": "X Axis Format", "renderTrigger": true, - "default": ".3s", + "default": "SMART_NUMBER", "choices": [ [ ".1s", @@ -2165,7 +2165,7 @@ "freeForm": true, "label": "Y Axis Format", "renderTrigger": true, - "default": ".3s", + "default": "SMART_NUMBER", "choices": [ [ ".1s", @@ -2206,7 +2206,7 @@ "type": "SelectControl", "freeForm": true, "label": "Right Axis Format", - "default": ".3s", + "default": "SMART_NUMBER", "choices": [ [ ".1s", diff --git a/superset/assets/src/explore/controlPanels/Bubble.js b/superset/assets/src/explore/controlPanels/Bubble.js index d3cdaca64..638e6d105 100644 --- a/superset/assets/src/explore/controlPanels/Bubble.js +++ b/superset/assets/src/explore/controlPanels/Bubble.js @@ -62,9 +62,6 @@ export default { }, ], controlOverrides: { - x_axis_format: { - default: '.3s', - }, color_scheme: { renderTrigger: false, }, diff --git a/superset/assets/src/explore/controls.jsx b/superset/assets/src/explore/controls.jsx index ae73e801c..c72348a09 100644 --- a/superset/assets/src/explore/controls.jsx +++ b/superset/assets/src/explore/controls.jsx @@ -79,6 +79,7 @@ const D3_FORMAT_DOCS = 'D3 format syntax: https://github.com/d3/d3-format'; // input choices & options const D3_FORMAT_OPTIONS = [ + ['SMART_NUMBER', 'Adaptative formating'], ['.1s', '.1s (12345.432 => 10k)'], ['.3s', '.3s (12345.432 => 12.3k)'], [',.1%', ',.1% (12345.432 => 1,234,543.2%)'], @@ -989,7 +990,7 @@ export const controls = { freeForm: true, label: t('Number format'), renderTrigger: true, - default: '.3s', + default: 'SMART_NUMBER', choices: D3_FORMAT_OPTIONS, description: D3_FORMAT_DOCS, }, @@ -1232,7 +1233,7 @@ export const controls = { freeForm: true, label: t('X Axis Format'), renderTrigger: true, - default: '.3s', + default: 'SMART_NUMBER', choices: D3_FORMAT_OPTIONS, description: D3_FORMAT_DOCS, }, @@ -1252,7 +1253,7 @@ export const controls = { freeForm: true, label: t('Y Axis Format'), renderTrigger: true, - default: '.3s', + default: 'SMART_NUMBER', choices: D3_FORMAT_OPTIONS, description: D3_FORMAT_DOCS, mapStateToProps: (state) => { @@ -1273,7 +1274,7 @@ export const controls = { type: 'SelectControl', freeForm: true, label: t('Right Axis Format'), - default: '.3s', + default: 'SMART_NUMBER', choices: D3_FORMAT_OPTIONS, description: D3_FORMAT_DOCS, }, From 74a0b57d8063189fb860c5d8317effc022e9b35c Mon Sep 17 00:00:00 2001 From: Kim Truong <47833996+khtruong@users.noreply.github.com> Date: Tue, 2 Apr 2019 15:00:11 -0700 Subject: [PATCH 09/38] Update necessary packages (#7205) --- superset/assets/package-lock.json | 37 ++++++++----------------------- superset/assets/package.json | 6 ++--- 2 files changed, 12 insertions(+), 31 deletions(-) diff --git a/superset/assets/package-lock.json b/superset/assets/package-lock.json index 18c3acf1c..3f19022e1 100644 --- a/superset/assets/package-lock.json +++ b/superset/assets/package-lock.json @@ -2232,9 +2232,9 @@ } }, "@superset-ui/color": { - "version": "0.10.8", - "resolved": "https://registry.npmjs.org/@superset-ui/color/-/color-0.10.8.tgz", - "integrity": "sha512-H1M8V9OKO3fCmOHQvW1rN9pRw2t/L1LKHvxzEj/Kccw+osckdmF8RtKEp7DaBuKMO6PF2Kq2FWNIiqNtin9whA==", + "version": "0.10.9", + "resolved": "https://registry.npmjs.org/@superset-ui/color/-/color-0.10.9.tgz", + "integrity": "sha512-/X+RFQtb//su5r7RIHZdDjy3dx+OZ2VOseqfeJXFjyte/4vLzSix3cA5Rj0iI8hU1tJz5mwt4q87M/MA4wDX6g==", "requires": { "@types/d3-scale": "^2.0.2", "d3-scale": "^2.1.2" @@ -2584,9 +2584,9 @@ } }, "@superset-ui/legacy-preset-chart-big-number": { - "version": "0.10.1", - "resolved": "https://registry.npmjs.org/@superset-ui/legacy-preset-chart-big-number/-/legacy-preset-chart-big-number-0.10.1.tgz", - "integrity": "sha512-hKVbVevvm9+py5v7f+z9pNXKu6bDcejWV2nJjxIubAkb4zQJOGiqBWiPKs2OXx6fhkmjxqFHB2I6nGaQUOAixg==", + "version": "0.10.4", + "resolved": "https://registry.npmjs.org/@superset-ui/legacy-preset-chart-big-number/-/legacy-preset-chart-big-number-0.10.4.tgz", + "integrity": "sha512-APMIWFFDRIkft6aep7c0VvFr9IV1ge1z+jYRF6UBYFNbwVmlxAKP7FN8amAsj/AAWYSoEyUd+PE0rjPED42oxw==", "requires": { "@data-ui/xy-chart": "^0.0.75", "d3-color": "^1.2.3", @@ -2595,9 +2595,9 @@ } }, "@superset-ui/legacy-preset-chart-nvd3": { - "version": "0.10.2", - "resolved": "https://registry.npmjs.org/@superset-ui/legacy-preset-chart-nvd3/-/legacy-preset-chart-nvd3-0.10.2.tgz", - "integrity": "sha512-szf++cq5cw7jbRZPSIZP6nS84UQg3ib3tlVl/68Sqy04sUuKD+MACgbn5/QyKIxnxSApdeL23zqaAU+DfA8pYA==", + "version": "0.10.4", + "resolved": "https://registry.npmjs.org/@superset-ui/legacy-preset-chart-nvd3/-/legacy-preset-chart-nvd3-0.10.4.tgz", + "integrity": "sha512-sQhsBookQevxuIKHHSPojZ4QqL02XWKfuhhpwhnLt487xJP94bYPJE2P/fZ/dZILkEfIjswmBML3GJ5XlvmJZw==", "requires": { "@data-ui/xy-chart": "^0.0.75", "d3": "^3.5.17", @@ -8684,7 +8684,6 @@ "ansi-regex": { "version": "2.1.1", "bundled": true, - "dev": true, "optional": true }, "aproba": { @@ -8706,13 +8705,11 @@ "balanced-match": { "version": "1.0.0", "bundled": true, - "dev": true, "optional": true }, "brace-expansion": { "version": "1.1.11", "bundled": true, - "dev": true, "optional": true, "requires": { "balanced-match": "^1.0.0", @@ -8728,19 +8725,16 @@ "code-point-at": { "version": "1.1.0", "bundled": true, - "dev": true, "optional": true }, "concat-map": { "version": "0.0.1", "bundled": true, - "dev": true, "optional": true }, "console-control-strings": { "version": "1.1.0", "bundled": true, - "dev": true, "optional": true }, "core-util-is": { @@ -8858,7 +8852,6 @@ "inherits": { "version": "2.0.3", "bundled": true, - "dev": true, "optional": true }, "ini": { @@ -8870,7 +8863,6 @@ "is-fullwidth-code-point": { "version": "1.0.0", "bundled": true, - "dev": true, "optional": true, "requires": { "number-is-nan": "^1.0.0" @@ -8885,7 +8877,6 @@ "minimatch": { "version": "3.0.4", "bundled": true, - "dev": true, "optional": true, "requires": { "brace-expansion": "^1.1.7" @@ -8894,13 +8885,11 @@ "minimist": { "version": "0.0.8", "bundled": true, - "dev": true, "optional": true }, "minipass": { "version": "2.2.4", "bundled": true, - "dev": true, "optional": true, "requires": { "safe-buffer": "^5.1.1", @@ -8919,7 +8908,6 @@ "mkdirp": { "version": "0.5.1", "bundled": true, - "dev": true, "optional": true, "requires": { "minimist": "0.0.8" @@ -9001,7 +8989,6 @@ "number-is-nan": { "version": "1.0.1", "bundled": true, - "dev": true, "optional": true }, "object-assign": { @@ -9013,7 +9000,6 @@ "once": { "version": "1.4.0", "bundled": true, - "dev": true, "optional": true, "requires": { "wrappy": "1" @@ -9100,7 +9086,6 @@ "safe-buffer": { "version": "5.1.1", "bundled": true, - "dev": true, "optional": true }, "safer-buffer": { @@ -9136,7 +9121,6 @@ "string-width": { "version": "1.0.2", "bundled": true, - "dev": true, "optional": true, "requires": { "code-point-at": "^1.0.0", @@ -9156,7 +9140,6 @@ "strip-ansi": { "version": "3.0.1", "bundled": true, - "dev": true, "optional": true, "requires": { "ansi-regex": "^2.0.0" @@ -9201,13 +9184,11 @@ "wrappy": { "version": "1.0.2", "bundled": true, - "dev": true, "optional": true }, "yallist": { "version": "3.0.2", "bundled": true, - "dev": true, "optional": true } } diff --git a/superset/assets/package.json b/superset/assets/package.json index 4a294cf14..d5a75d45d 100644 --- a/superset/assets/package.json +++ b/superset/assets/package.json @@ -49,7 +49,7 @@ "dependencies": { "@data-ui/sparkline": "^0.0.54", "@superset-ui/chart": "^0.10.2", - "@superset-ui/color": "^0.10.1", + "@superset-ui/color": "^0.10.9", "@superset-ui/connection": "^0.10.2", "@superset-ui/core": "^0.10.0", "@superset-ui/dimension": "^0.10.0", @@ -75,8 +75,8 @@ "@superset-ui/legacy-plugin-chart-treemap": "^0.10.0", "@superset-ui/legacy-plugin-chart-word-cloud": "^0.10.0", "@superset-ui/legacy-plugin-chart-world-map": "^0.10.0", - "@superset-ui/legacy-preset-chart-big-number": "^0.10.1", - "@superset-ui/legacy-preset-chart-nvd3": "^0.10.2", + "@superset-ui/legacy-preset-chart-big-number": "^0.10.4", + "@superset-ui/legacy-preset-chart-nvd3": "^0.10.4", "@superset-ui/number-format": "^0.10.1", "@superset-ui/time-format": "^0.10.1", "@superset-ui/translation": "^0.10.0", From 200efacc3649d10a56ae14724d9b522a44839fe0 Mon Sep 17 00:00:00 2001 From: Kim Truong <47833996+khtruong@users.noreply.github.com> Date: Tue, 2 Apr 2019 17:13:18 -0700 Subject: [PATCH 10/38] fix: [lyftga] pass the latest deckgl props (#7208) --- .../deckgl/AnimatableDeckGLContainer.jsx | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/superset/assets/src/visualizations/deckgl/AnimatableDeckGLContainer.jsx b/superset/assets/src/visualizations/deckgl/AnimatableDeckGLContainer.jsx index f206522cf..fe2c7165d 100644 --- a/superset/assets/src/visualizations/deckgl/AnimatableDeckGLContainer.jsx +++ b/superset/assets/src/visualizations/deckgl/AnimatableDeckGLContainer.jsx @@ -34,6 +34,9 @@ const propTypes = { disabled: PropTypes.bool, viewport: PropTypes.object.isRequired, children: PropTypes.node, + mapStyle: PropTypes.string, + mapboxApiAccessToken: PropTypes.string.isRequired, + setControlValue: PropTypes.func, onViewportChange: PropTypes.func, onValuesChange: PropTypes.func, }; @@ -41,6 +44,8 @@ const propTypes = { const defaultProps = { aggregation: false, disabled: false, + mapStyle: 'light', + setControlValue: () => {}, onViewportChange: () => {}, onValuesChange: () => {}, }; @@ -48,9 +53,6 @@ const defaultProps = { export default class AnimatableDeckGLContainer extends React.Component { constructor(props) { super(props); - const { getLayers, start, end, getStep, values, disabled, viewport, ...other } = props; - this.other = other; - this.onViewportChange = this.onViewportChange.bind(this); } onViewportChange(viewport) { @@ -71,6 +73,9 @@ export default class AnimatableDeckGLContainer extends React.Component { values, onValuesChange, viewport, + setControlValue, + mapStyle, + mapboxApiAccessToken, } = this.props; const layers = getLayers(values); @@ -83,9 +88,11 @@ export default class AnimatableDeckGLContainer extends React.Component { return (
{!disabled && From 8e14807b2a37d03ec9ce33459f4afe6917354b96 Mon Sep 17 00:00:00 2001 From: Christine Chambers Date: Wed, 3 Apr 2019 11:19:56 -0700 Subject: [PATCH 11/38] chore: revert bignumber.js patch for charts and reapply the original bignumber.js change to SQL editor in an opt-in fashion (#7210) * revert: bignumber patch to prevent rendering regression in charts * Revert "Fix rendering regression from the introduction of bignumber (#6937)" * fix: consume the bignumber.js reversion -- apply the bignumber conversion in `actions/sqlLab.js` where it is needed (aka opt into bignumber.js). * Revert "Fix deck.gl form data (#6953)" b/c formData now returns snake_cased properties for deck vizzes. --- superset/assets/package-lock.json | 458 ++++++++++-------- superset/assets/package.json | 3 +- .../chart/transformBigNumber_spec.js | 53 -- .../visualizations/deckgl/utils_spec.js | 20 +- superset/assets/src/SqlLab/actions/sqlLab.js | 13 +- superset/assets/src/chart/ChartRenderer.jsx | 15 - .../assets/src/chart/transformBigNumber.js | 45 -- .../deckgl/CategoricalDeckGLContainer.jsx | 18 +- .../src/visualizations/deckgl/Multi/Multi.jsx | 6 +- .../src/visualizations/deckgl/factory.jsx | 2 +- .../visualizations/deckgl/layers/Arc/Arc.jsx | 8 +- .../deckgl/layers/Geojson/Geojson.jsx | 14 +- .../deckgl/layers/Grid/Grid.jsx | 12 +- .../visualizations/deckgl/layers/Hex/Hex.jsx | 12 +- .../deckgl/layers/Path/Path.jsx | 10 +- .../deckgl/layers/Polygon/Polygon.jsx | 26 +- .../deckgl/layers/Scatter/Scatter.jsx | 10 +- .../deckgl/layers/Screengrid/Screengrid.jsx | 14 +- .../visualizations/deckgl/layers/common.jsx | 16 +- .../assets/src/visualizations/deckgl/utils.js | 14 +- 20 files changed, 359 insertions(+), 410 deletions(-) delete mode 100644 superset/assets/spec/javascripts/chart/transformBigNumber_spec.js delete mode 100644 superset/assets/src/chart/transformBigNumber.js diff --git a/superset/assets/package-lock.json b/superset/assets/package-lock.json index 3f19022e1..35b3c3ad0 100644 --- a/superset/assets/package-lock.json +++ b/superset/assets/package-lock.json @@ -1368,7 +1368,7 @@ }, "@data-ui/event-flow": { "version": "0.0.54", - "resolved": "http://registry.npmjs.org/@data-ui/event-flow/-/event-flow-0.0.54.tgz", + "resolved": "https://registry.npmjs.org/@data-ui/event-flow/-/event-flow-0.0.54.tgz", "integrity": "sha1-uwPh/StWNCSGVbjfnTxsOKdH5l4=", "requires": { "@data-ui/forms": "0.0.50", @@ -1659,7 +1659,7 @@ }, "@data-ui/radial-chart": { "version": "0.0.54", - "resolved": "http://registry.npmjs.org/@data-ui/radial-chart/-/radial-chart-0.0.54.tgz", + "resolved": "https://registry.npmjs.org/@data-ui/radial-chart/-/radial-chart-0.0.54.tgz", "integrity": "sha1-DSiwdoHZtgJ9msI7cpJBgn1RMAE=", "requires": { "@data-ui/shared": "0.0.54", @@ -1674,7 +1674,7 @@ }, "@data-ui/shared": { "version": "0.0.54", - "resolved": "http://registry.npmjs.org/@data-ui/shared/-/shared-0.0.54.tgz", + "resolved": "https://registry.npmjs.org/@data-ui/shared/-/shared-0.0.54.tgz", "integrity": "sha1-L7DW3ukNrCC/jzwpE8aFCoIj1Zs=", "requires": { "@data-ui/theme": "0.0.48", @@ -1747,7 +1747,7 @@ }, "@data-ui/sparkline": { "version": "0.0.54", - "resolved": "http://registry.npmjs.org/@data-ui/sparkline/-/sparkline-0.0.54.tgz", + "resolved": "https://registry.npmjs.org/@data-ui/sparkline/-/sparkline-0.0.54.tgz", "integrity": "sha1-zj0WbZ4LI5oLoC84lMuejIQXHO8=", "requires": { "@data-ui/shared": "0.0.54", @@ -2201,7 +2201,7 @@ }, "@sinonjs/formatio": { "version": "2.0.0", - "resolved": "http://registry.npmjs.org/@sinonjs/formatio/-/formatio-2.0.0.tgz", + "resolved": "https://registry.npmjs.org/@sinonjs/formatio/-/formatio-2.0.0.tgz", "integrity": "sha512-ls6CAMA6/5gG+O/IdsBcblvnd8qcO/l1TYoNeAzp3wcISOxlPXQEus0mLcdwazEkWjaBdaJ3TaxmNgCLWwvWzg==", "dev": true, "requires": { @@ -2220,9 +2220,9 @@ } }, "@superset-ui/chart": { - "version": "0.10.2", - "resolved": "https://registry.npmjs.org/@superset-ui/chart/-/chart-0.10.2.tgz", - "integrity": "sha512-SHWnZlLecEn9Ny9rUp0kYcPuIFLxZtBC0yY1wnRIiO974qHPm02nvC+IhgGAs+GlNN302WyrIBQzk2aci6UGhw==", + "version": "0.10.8", + "resolved": "https://registry.npmjs.org/@superset-ui/chart/-/chart-0.10.8.tgz", + "integrity": "sha512-O/i0o/Y5E2tlAEOWHgZaEpnXDqX25Q/xcrgMKOBjbcCUeGMysekvcs7hMkYVI/vTt15sJY3R318kXKNWX6RkKA==", "requires": { "@types/react": "^16.7.17", "@types/react-loadable": "^5.4.2", @@ -2241,34 +2241,33 @@ } }, "@superset-ui/connection": { - "version": "0.10.2", - "resolved": "https://registry.npmjs.org/@superset-ui/connection/-/connection-0.10.2.tgz", - "integrity": "sha512-G+s+EnIY+GZiV621eUXoe2rK8OP74MkMjQEf5KtNHZNDZVuOLEIINPrdAbqo13ZOdwtGKEEnYWP3opOSiVDReg==", + "version": "0.11.0", + "resolved": "https://registry.npmjs.org/@superset-ui/connection/-/connection-0.11.0.tgz", + "integrity": "sha512-S5fe3DVUKehhGvhjR/jOxitKJ6W91PlB1jYRCdTrDz7/FcrQSgV3okUBnyMeQApInh2Civ/vI0ehcb4+7dNqOg==", "requires": { "@babel/runtime": "^7.1.2", - "json-bigint": "^0.3.0", "whatwg-fetch": "^2.0.4" }, "dependencies": { "whatwg-fetch": { "version": "2.0.4", - "resolved": "http://registry.npmjs.org/whatwg-fetch/-/whatwg-fetch-2.0.4.tgz", + "resolved": "https://registry.npmjs.org/whatwg-fetch/-/whatwg-fetch-2.0.4.tgz", "integrity": "sha512-dcQ1GWpOD/eEQ97k66aiEVpNnapVj90/+R+SXTPYGHpYBBypfKJEQjLrvMZ7YXbKm21gXd4NcuxUTjiv1YtLng==" } } }, "@superset-ui/core": { - "version": "0.10.0", - "resolved": "https://registry.npmjs.org/@superset-ui/core/-/core-0.10.0.tgz", - "integrity": "sha512-yz1Mywsrd0QZTwp7tfNzi/mB2jMnSkd+Ht7/MPGqjbuJLGgGCywKpvTZ5TTvmTEl5th0xGDBpaSodPWN31lb1w==", + "version": "0.10.6", + "resolved": "https://registry.npmjs.org/@superset-ui/core/-/core-0.10.6.tgz", + "integrity": "sha512-BYKkz1zyV3PykuvIM/3XqNk7dfTEBCvR+Kxd//6w5Z2/JPlnhp+t4Nm7s4g6tLdtW76LcL7JwvBO14wMSfsNvw==", "requires": { "lodash": "^4.17.11" } }, "@superset-ui/dimension": { - "version": "0.10.0", - "resolved": "https://registry.npmjs.org/@superset-ui/dimension/-/dimension-0.10.0.tgz", - "integrity": "sha512-4ubDo+/NAajaH3YyOOL+G3HXjJ1EFN+rjLnAgyUumovX5aYNET/15Uqsvqi04Nm8+EMKmyE7IxQ2YYXVOLCnUw==" + "version": "0.10.4", + "resolved": "https://registry.npmjs.org/@superset-ui/dimension/-/dimension-0.10.4.tgz", + "integrity": "sha512-+VDPb2bOJS6H/zxh/MC+WSgXemQjPx8GMNu+LKbbgaBYCg1xyo/ldqPdOCo+Gr4YgjUwyCNfxRG4W7Fzd1NvfA==" }, "@superset-ui/legacy-plugin-chart-calendar": { "version": "0.10.0", @@ -2714,9 +2713,9 @@ } }, "@types/react-loadable": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/@types/react-loadable/-/react-loadable-5.5.0.tgz", - "integrity": "sha512-evH/O6Wytz6lSmi36YL7YHnFc46zRPSA8+XNtTXI4D7A/CP7OVwcWbHjb2yGbh/jDxxhnTwIVO/PeqDXtUegnQ==", + "version": "5.5.1", + "resolved": "https://registry.npmjs.org/@types/react-loadable/-/react-loadable-5.5.1.tgz", + "integrity": "sha512-PSmh6IT9vHeO9QjhApMMiJ65T0Amrpf3fom+04ur872IdgCZK9MzjeN3Q11bzgQMkrV448sbT8WopQw9o0LX1g==", "requires": { "@types/react": "*", "@types/webpack": "*" @@ -2748,9 +2747,9 @@ "integrity": "sha512-iHI60IbyfQilNubmxsq4zqSjdynlmc2Q/QvH9kjzg9+CCYVVzq1O6tc7VBzSygIwnmOt07w80IG6HDQvjv3Liw==" }, "@types/webpack": { - "version": "4.4.25", - "resolved": "https://registry.npmjs.org/@types/webpack/-/webpack-4.4.25.tgz", - "integrity": "sha512-YaYVbSK1bC3xiAWFLSgDQyVHdCTNq5cLlcx633basmrwSoUxJiv4SZ0SoT1uoF15zWx98afOcCbqA1YHeCdRYA==", + "version": "4.4.26", + "resolved": "https://registry.npmjs.org/@types/webpack/-/webpack-4.4.26.tgz", + "integrity": "sha512-vs8LjgEZUQTBxotXbMf8s4jgykozkqjv6P0JRi+1BLh0n7LQUkMXfvsoPb5U/dBL1ay5Lu0c46G6FRmAZBhAUA==", "requires": { "@types/anymatch": "*", "@types/node": "*", @@ -3308,7 +3307,7 @@ }, "acorn-jsx": { "version": "3.0.1", - "resolved": "http://registry.npmjs.org/acorn-jsx/-/acorn-jsx-3.0.1.tgz", + "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-3.0.1.tgz", "integrity": "sha1-r9+UiPsezvyDSPb7IvRk4ypYs2s=", "dev": true, "requires": { @@ -3317,7 +3316,7 @@ "dependencies": { "acorn": { "version": "3.3.0", - "resolved": "http://registry.npmjs.org/acorn/-/acorn-3.3.0.tgz", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-3.3.0.tgz", "integrity": "sha1-ReN/s56No/JbruP/U2niu18iAXo=", "dev": true } @@ -3752,7 +3751,7 @@ }, "array-equal": { "version": "1.0.0", - "resolved": "http://registry.npmjs.org/array-equal/-/array-equal-1.0.0.tgz", + "resolved": "https://registry.npmjs.org/array-equal/-/array-equal-1.0.0.tgz", "integrity": "sha1-jCpe8kcv2ep0KwTHenUJO6J1fJM=", "dev": true }, @@ -3875,7 +3874,7 @@ }, "util": { "version": "0.10.3", - "resolved": "http://registry.npmjs.org/util/-/util-0.10.3.tgz", + "resolved": "https://registry.npmjs.org/util/-/util-0.10.3.tgz", "integrity": "sha1-evsa/lCAUkZInj23/g7TeTNqwPk=", "dev": true, "requires": { @@ -4365,7 +4364,7 @@ }, "babel-plugin-syntax-dynamic-import": { "version": "6.18.0", - "resolved": "http://registry.npmjs.org/babel-plugin-syntax-dynamic-import/-/babel-plugin-syntax-dynamic-import-6.18.0.tgz", + "resolved": "https://registry.npmjs.org/babel-plugin-syntax-dynamic-import/-/babel-plugin-syntax-dynamic-import-6.18.0.tgz", "integrity": "sha1-jWomIpyDdFqZgqRBBRVyyqF5sdo=", "dev": true }, @@ -4562,9 +4561,9 @@ "integrity": "sha512-+hN/Zh2D08Mx65pZ/4g5bsmNiZUuChDiQfTUQ7qJr4/kuopCr88xZsAXv6mBoZEsUI4OuGHlX59qE94K2mMW8Q==" }, "bignumber.js": { - "version": "8.1.1", - "resolved": "https://registry.npmjs.org/bignumber.js/-/bignumber.js-8.1.1.tgz", - "integrity": "sha512-QD46ppGintwPGuL1KqmwhR0O+N2cZUg8JG/VzwI2e28sM9TqHjQB10lI4QAaMHVbLzwVLLAwEglpKPViWX+5NQ==" + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/bignumber.js/-/bignumber.js-2.4.0.tgz", + "integrity": "sha1-g4qZLan51zfg9LLbC+YrsJ3Qxeg=" }, "binary-extensions": { "version": "1.12.0", @@ -4649,7 +4648,7 @@ }, "brace": { "version": "0.11.1", - "resolved": "http://registry.npmjs.org/brace/-/brace-0.11.1.tgz", + "resolved": "https://registry.npmjs.org/brace/-/brace-0.11.1.tgz", "integrity": "sha1-SJb8ydVE7vRfS7dmDbMg07N5/lg=" }, "brace-expansion": { @@ -4681,7 +4680,7 @@ }, "brfs": { "version": "1.6.1", - "resolved": "http://registry.npmjs.org/brfs/-/brfs-1.6.1.tgz", + "resolved": "https://registry.npmjs.org/brfs/-/brfs-1.6.1.tgz", "integrity": "sha512-OfZpABRQQf+Xsmju8XE9bDjs+uU4vLREGolP7bDgcpsI17QREyZ4Bl+2KLxxx1kCgA0fAIhKQBaBYh+PEcCqYQ==", "requires": { "quote-stream": "^1.0.1", @@ -4713,7 +4712,7 @@ "dependencies": { "resolve": { "version": "1.1.7", - "resolved": "http://registry.npmjs.org/resolve/-/resolve-1.1.7.tgz", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.1.7.tgz", "integrity": "sha1-IDEU2CrSxe2ejgQRs5ModeiJ6Xs=", "dev": true } @@ -4721,7 +4720,7 @@ }, "browserify-aes": { "version": "1.2.0", - "resolved": "http://registry.npmjs.org/browserify-aes/-/browserify-aes-1.2.0.tgz", + "resolved": "https://registry.npmjs.org/browserify-aes/-/browserify-aes-1.2.0.tgz", "integrity": "sha512-+7CHXqGuspUn/Sl5aO7Ea0xWGAtETPXNSAjHo48JfLdPWcMng33Xe4znFvQweqc/uzk5zSOI3H52CYnjCfb5hA==", "dev": true, "requires": { @@ -4758,7 +4757,7 @@ }, "browserify-rsa": { "version": "4.0.1", - "resolved": "http://registry.npmjs.org/browserify-rsa/-/browserify-rsa-4.0.1.tgz", + "resolved": "https://registry.npmjs.org/browserify-rsa/-/browserify-rsa-4.0.1.tgz", "integrity": "sha1-IeCr+vbyApzy+vsTNWenAdQTVSQ=", "dev": true, "requires": { @@ -4810,7 +4809,7 @@ }, "buffer": { "version": "4.9.1", - "resolved": "http://registry.npmjs.org/buffer/-/buffer-4.9.1.tgz", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-4.9.1.tgz", "integrity": "sha1-bRu2AbB6TvztlwlBMgkwJ8lbwpg=", "dev": true, "requires": { @@ -4943,7 +4942,7 @@ "dependencies": { "callsites": { "version": "2.0.0", - "resolved": "http://registry.npmjs.org/callsites/-/callsites-2.0.0.tgz", + "resolved": "https://registry.npmjs.org/callsites/-/callsites-2.0.0.tgz", "integrity": "sha1-BuuE8A7qQT2oav/vrL/7Ngk7PFA=", "dev": true } @@ -5262,7 +5261,7 @@ }, "clean-webpack-plugin": { "version": "0.1.19", - "resolved": "http://registry.npmjs.org/clean-webpack-plugin/-/clean-webpack-plugin-0.1.19.tgz", + "resolved": "https://registry.npmjs.org/clean-webpack-plugin/-/clean-webpack-plugin-0.1.19.tgz", "integrity": "sha512-M1Li5yLHECcN2MahoreuODul5LkjohJGFxLPTjl3j1ttKrF5rgjZET1SJduuqxLAuT1gAPOdkhg03qcaaU1KeA==", "dev": true, "requires": { @@ -5673,7 +5672,7 @@ }, "create-hash": { "version": "1.2.0", - "resolved": "http://registry.npmjs.org/create-hash/-/create-hash-1.2.0.tgz", + "resolved": "https://registry.npmjs.org/create-hash/-/create-hash-1.2.0.tgz", "integrity": "sha512-z00bCGNHDG8mHAkP7CtT1qVu+bFQUPjYq/4Iv3C3kWjTFV10zIjfSoeqXo9Asws8gwSHDGj/hl2u4OGIjapeCg==", "dev": true, "requires": { @@ -5686,7 +5685,7 @@ }, "create-hmac": { "version": "1.1.7", - "resolved": "http://registry.npmjs.org/create-hmac/-/create-hmac-1.1.7.tgz", + "resolved": "https://registry.npmjs.org/create-hmac/-/create-hmac-1.1.7.tgz", "integrity": "sha512-MJG9liiZ+ogc4TzUwuvbER1JRdgvUFSB5+VR/g5h82fGaIRWMWddtKBHi7/sVhfjQZ6SehlyhvQYrcYkaUIpLg==", "dev": true, "requires": { @@ -5747,7 +5746,7 @@ }, "css-color-names": { "version": "0.0.4", - "resolved": "http://registry.npmjs.org/css-color-names/-/css-color-names-0.0.4.tgz", + "resolved": "https://registry.npmjs.org/css-color-names/-/css-color-names-0.0.4.tgz", "integrity": "sha1-gIrcLnnPhHOAabZGyyDsJ762KeA=", "dev": true }, @@ -5811,7 +5810,7 @@ }, "css-in-js-utils": { "version": "2.0.1", - "resolved": "http://registry.npmjs.org/css-in-js-utils/-/css-in-js-utils-2.0.1.tgz", + "resolved": "https://registry.npmjs.org/css-in-js-utils/-/css-in-js-utils-2.0.1.tgz", "integrity": "sha512-PJF0SpJT+WdbVVt0AOYp9C8GnuruRlL/UFW7932nLWmFLQTaWEzTBQEx7/hn4BuV+WON75iAViSUJLiU3PKbpA==", "requires": { "hyphenate-style-name": "^1.0.2", @@ -5871,7 +5870,7 @@ }, "css-select": { "version": "1.2.0", - "resolved": "http://registry.npmjs.org/css-select/-/css-select-1.2.0.tgz", + "resolved": "https://registry.npmjs.org/css-select/-/css-select-1.2.0.tgz", "integrity": "sha1-KzoRBTnFNV8c2NMUYj6HCxIeyFg=", "dev": true, "requires": { @@ -6259,7 +6258,7 @@ }, "d3-geo-projection": { "version": "0.2.16", - "resolved": "http://registry.npmjs.org/d3-geo-projection/-/d3-geo-projection-0.2.16.tgz", + "resolved": "https://registry.npmjs.org/d3-geo-projection/-/d3-geo-projection-0.2.16.tgz", "integrity": "sha1-SZTs0QM92xUztsTFUoocgdzClCc=", "requires": { "brfs": "^1.3.0" @@ -6677,7 +6676,7 @@ }, "diffie-hellman": { "version": "5.0.3", - "resolved": "http://registry.npmjs.org/diffie-hellman/-/diffie-hellman-5.0.3.tgz", + "resolved": "https://registry.npmjs.org/diffie-hellman/-/diffie-hellman-5.0.3.tgz", "integrity": "sha512-kqag/Nl+f3GwyK25fhUMYj81BUOrZ9IuJsjIcDE5icNM9FJHAVm3VcUDxdLPoQtTuUylWm6ZIknYJwwaPxsUzg==", "dev": true, "requires": { @@ -6707,7 +6706,7 @@ }, "dnd-core": { "version": "2.6.0", - "resolved": "http://registry.npmjs.org/dnd-core/-/dnd-core-2.6.0.tgz", + "resolved": "https://registry.npmjs.org/dnd-core/-/dnd-core-2.6.0.tgz", "integrity": "sha1-ErrWbVh0LG5ffPKUP7aFlED4CcQ=", "requires": { "asap": "^2.0.6", @@ -6837,7 +6836,7 @@ }, "duplexer": { "version": "0.1.1", - "resolved": "http://registry.npmjs.org/duplexer/-/duplexer-0.1.1.tgz", + "resolved": "https://registry.npmjs.org/duplexer/-/duplexer-0.1.1.tgz", "integrity": "sha1-rOb/gIwc5mtX0ev5eXessCM0z8E=", "dev": true }, @@ -7079,7 +7078,7 @@ }, "es6-promise": { "version": "3.3.1", - "resolved": "http://registry.npmjs.org/es6-promise/-/es6-promise-3.3.1.tgz", + "resolved": "https://registry.npmjs.org/es6-promise/-/es6-promise-3.3.1.tgz", "integrity": "sha1-oIzd6EzNvzTQJ6FFG8kdS80ophM=" }, "es6bindall": { @@ -7126,7 +7125,7 @@ }, "eslint": { "version": "4.19.1", - "resolved": "http://registry.npmjs.org/eslint/-/eslint-4.19.1.tgz", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-4.19.1.tgz", "integrity": "sha512-bT3/1x1EbZB7phzYu7vCr1v3ONuzDtX8WjuM9c0iYxe+cq+pwcKEoQjl7zd3RpC6YOLgnSy3cTN58M2jcoPDIQ==", "dev": true, "requires": { @@ -7230,7 +7229,7 @@ }, "fast-deep-equal": { "version": "1.1.0", - "resolved": "http://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-1.1.0.tgz", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-1.1.0.tgz", "integrity": "sha1-wFNHeBfIa1HaqFPIHgWbcz0CNhQ=", "dev": true }, @@ -7404,7 +7403,7 @@ "dependencies": { "doctrine": { "version": "1.5.0", - "resolved": "http://registry.npmjs.org/doctrine/-/doctrine-1.5.0.tgz", + "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-1.5.0.tgz", "integrity": "sha1-N53Ocw9hZvds76TmcHoVmwLFpvo=", "dev": true, "requires": { @@ -7414,7 +7413,7 @@ }, "load-json-file": { "version": "2.0.0", - "resolved": "http://registry.npmjs.org/load-json-file/-/load-json-file-2.0.0.tgz", + "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-2.0.0.tgz", "integrity": "sha1-eUfkIUmvgNaWy/eXvKq8/h/inKg=", "dev": true, "requires": { @@ -7552,7 +7551,7 @@ }, "espree": { "version": "3.5.4", - "resolved": "http://registry.npmjs.org/espree/-/espree-3.5.4.tgz", + "resolved": "https://registry.npmjs.org/espree/-/espree-3.5.4.tgz", "integrity": "sha512-yAcIQxtmMiB/jL32dzEp2enBeidsB7xWPLNiw3IIkpVds1P+h7qF9YwJq1yUNzp2OKXgAprs4F61ih66UsoD1A==", "dev": true, "requires": { @@ -7732,7 +7731,7 @@ "dependencies": { "source-map": { "version": "0.5.0", - "resolved": "http://registry.npmjs.org/source-map/-/source-map-0.5.0.tgz", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.0.tgz", "integrity": "sha1-D+llA6yGpa213mP05BKuSHLNvoY=", "dev": true } @@ -7778,7 +7777,7 @@ "dependencies": { "array-flatten": { "version": "1.1.1", - "resolved": "http://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", + "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", "integrity": "sha1-ml9pkFGx5wczKPKgCJaLZOopVdI=", "dev": true }, @@ -7806,7 +7805,7 @@ }, "external-editor": { "version": "2.2.0", - "resolved": "http://registry.npmjs.org/external-editor/-/external-editor-2.2.0.tgz", + "resolved": "https://registry.npmjs.org/external-editor/-/external-editor-2.2.0.tgz", "integrity": "sha512-bSn6gvGxKt+b7+6TKEv1ZycHleA7aHhRHyAqJyp5pbUFuYYNIzpZnQDk7AsYckyWdEnTeAnay0aCy2aV6iTk9A==", "dev": true, "requires": { @@ -8028,7 +8027,7 @@ }, "file-loader": { "version": "1.1.11", - "resolved": "http://registry.npmjs.org/file-loader/-/file-loader-1.1.11.tgz", + "resolved": "https://registry.npmjs.org/file-loader/-/file-loader-1.1.11.tgz", "integrity": "sha512-TGR4HU7HUsGg6GCOPJnFk06RhWgEWFLAGWiT6rcD+GRC2keU3s9RGJ+b3Z6/U73jwwNb2gKLJ7YCrp+jvU4ALg==", "dev": true, "requires": { @@ -8038,7 +8037,7 @@ }, "file-type": { "version": "3.9.0", - "resolved": "http://registry.npmjs.org/file-type/-/file-type-3.9.0.tgz", + "resolved": "https://registry.npmjs.org/file-type/-/file-type-3.9.0.tgz", "integrity": "sha1-JXoHg4TR24CHvESdEH1SpSZyuek=" }, "fileset": { @@ -8071,7 +8070,7 @@ }, "finalhandler": { "version": "1.1.1", - "resolved": "http://registry.npmjs.org/finalhandler/-/finalhandler-1.1.1.tgz", + "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.1.1.tgz", "integrity": "sha512-Y1GUDo39ez4aHAw7MysnUD5JzYX+WaIj8I57kO3aEPT1fFRL4sr7mjei97FgnwhAyyzRYmQZaTHb2+9uZ1dPtg==", "dev": true, "requires": { @@ -8677,24 +8676,28 @@ "dependencies": { "abbrev": { "version": "1.1.1", - "bundled": true, + "resolved": false, + "integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==", "dev": true, "optional": true }, "ansi-regex": { "version": "2.1.1", - "bundled": true, - "optional": true + "resolved": false, + "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=", + "dev": true }, "aproba": { "version": "1.2.0", - "bundled": true, + "resolved": false, + "integrity": "sha512-Y9J6ZjXtoYh8RnXVCMOU/ttDmk1aBjunq9vO0ta5x85WDQiQfUF9sIPBITdbiiIVcBo03Hi3jMxigBtsddlXRw==", "dev": true, "optional": true }, "are-we-there-yet": { "version": "1.1.4", - "bundled": true, + "resolved": false, + "integrity": "sha1-u13KOCu5TwXhUZQ3PRb9O6HKEQ0=", "dev": true, "optional": true, "requires": { @@ -8704,13 +8707,15 @@ }, "balanced-match": { "version": "1.0.0", - "bundled": true, - "optional": true + "resolved": false, + "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=", + "dev": true }, "brace-expansion": { "version": "1.1.11", - "bundled": true, - "optional": true, + "resolved": false, + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dev": true, "requires": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" @@ -8718,34 +8723,40 @@ }, "chownr": { "version": "1.0.1", - "bundled": true, + "resolved": false, + "integrity": "sha1-4qdQQqlVGQi+vSW4Uj1fl2nXkYE=", "dev": true, "optional": true }, "code-point-at": { "version": "1.1.0", - "bundled": true, - "optional": true + "resolved": false, + "integrity": "sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c=", + "dev": true }, "concat-map": { "version": "0.0.1", - "bundled": true, - "optional": true + "resolved": false, + "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=", + "dev": true }, "console-control-strings": { "version": "1.1.0", - "bundled": true, - "optional": true + "resolved": false, + "integrity": "sha1-PXz0Rk22RG6mRL9LOVB/mFEAjo4=", + "dev": true }, "core-util-is": { "version": "1.0.2", - "bundled": true, + "resolved": false, + "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=", "dev": true, "optional": true }, "debug": { "version": "2.6.9", - "bundled": true, + "resolved": false, + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", "dev": true, "optional": true, "requires": { @@ -8754,25 +8765,29 @@ }, "deep-extend": { "version": "0.5.1", - "bundled": true, + "resolved": false, + "integrity": "sha512-N8vBdOa+DF7zkRrDCsaOXoCs/E2fJfx9B9MrKnnSiHNh4ws7eSys6YQE4KvT1cecKmOASYQBhbKjeuDD9lT81w==", "dev": true, "optional": true }, "delegates": { "version": "1.0.0", - "bundled": true, + "resolved": false, + "integrity": "sha1-hMbhWbgZBP3KWaDvRM2HDTElD5o=", "dev": true, "optional": true }, "detect-libc": { "version": "1.0.3", - "bundled": true, + "resolved": false, + "integrity": "sha1-+hN8S9aY7fVc1c0CrFWfkaTEups=", "dev": true, "optional": true }, "fs-minipass": { "version": "1.2.5", - "bundled": true, + "resolved": false, + "integrity": "sha512-JhBl0skXjUPCFH7x6x61gQxrKyXsxB5gcgePLZCwfyCGGsTISMoIeObbrvVeP6Xmyaudw4TT43qV2Gz+iyd2oQ==", "dev": true, "optional": true, "requires": { @@ -8781,13 +8796,15 @@ }, "fs.realpath": { "version": "1.0.0", - "bundled": true, + "resolved": false, + "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=", "dev": true, "optional": true }, "gauge": { "version": "2.7.4", - "bundled": true, + "resolved": false, + "integrity": "sha1-LANAXHU4w51+s3sxcCLjJfsBi/c=", "dev": true, "optional": true, "requires": { @@ -8803,7 +8820,8 @@ }, "glob": { "version": "7.1.2", - "bundled": true, + "resolved": false, + "integrity": "sha512-MJTUg1kjuLeQCJ+ccE4Vpa6kKVXkPYJ2mOCQyUuKLcLQsdrMCpBPUi8qVE6+YuaJkozeA9NusTAw3hLr8Xe5EQ==", "dev": true, "optional": true, "requires": { @@ -8817,13 +8835,15 @@ }, "has-unicode": { "version": "2.0.1", - "bundled": true, + "resolved": false, + "integrity": "sha1-4Ob+aijPUROIVeCG0Wkedx3iqLk=", "dev": true, "optional": true }, "iconv-lite": { "version": "0.4.21", - "bundled": true, + "resolved": false, + "integrity": "sha512-En5V9za5mBt2oUA03WGD3TwDv0MKAruqsuxstbMUZaj9W9k/m1CV/9py3l0L5kw9Bln8fdHQmzHSYtvpvTLpKw==", "dev": true, "optional": true, "requires": { @@ -8832,7 +8852,8 @@ }, "ignore-walk": { "version": "3.0.1", - "bundled": true, + "resolved": false, + "integrity": "sha512-DTVlMx3IYPe0/JJcYP7Gxg7ttZZu3IInhuEhbchuqneY9wWe5Ojy2mXLBaQFUQmo0AW2r3qG7m1mg86js+gnlQ==", "dev": true, "optional": true, "requires": { @@ -8841,7 +8862,8 @@ }, "inflight": { "version": "1.0.6", - "bundled": true, + "resolved": false, + "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", "dev": true, "optional": true, "requires": { @@ -8851,46 +8873,53 @@ }, "inherits": { "version": "2.0.3", - "bundled": true, - "optional": true + "resolved": false, + "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=", + "dev": true }, "ini": { "version": "1.3.5", - "bundled": true, + "resolved": false, + "integrity": "sha512-RZY5huIKCMRWDUqZlEi72f/lmXKMvuszcMBduliQ3nnWbx9X/ZBQO7DijMEYS9EhHBb2qacRUMtC7svLwe0lcw==", "dev": true, "optional": true }, "is-fullwidth-code-point": { "version": "1.0.0", - "bundled": true, - "optional": true, + "resolved": false, + "integrity": "sha1-754xOG8DGn8NZDr4L95QxFfvAMs=", + "dev": true, "requires": { "number-is-nan": "^1.0.0" } }, "isarray": { "version": "1.0.0", - "bundled": true, + "resolved": false, + "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=", "dev": true, "optional": true }, "minimatch": { "version": "3.0.4", - "bundled": true, - "optional": true, + "resolved": false, + "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", + "dev": true, "requires": { "brace-expansion": "^1.1.7" } }, "minimist": { "version": "0.0.8", - "bundled": true, - "optional": true + "resolved": false, + "integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=", + "dev": true }, "minipass": { "version": "2.2.4", - "bundled": true, - "optional": true, + "resolved": false, + "integrity": "sha512-hzXIWWet/BzWhYs2b+u7dRHlruXhwdgvlTMDKC6Cb1U7ps6Ac6yQlR39xsbjWJE377YTCtKwIXIpJ5oP+j5y8g==", + "dev": true, "requires": { "safe-buffer": "^5.1.1", "yallist": "^3.0.0" @@ -8898,7 +8927,8 @@ }, "minizlib": { "version": "1.1.0", - "bundled": true, + "resolved": false, + "integrity": "sha512-4T6Ur/GctZ27nHfpt9THOdRZNgyJ9FZchYO1ceg5S8Q3DNLCKYy44nCZzgCJgcvx2UM8czmqak5BCxJMrq37lA==", "dev": true, "optional": true, "requires": { @@ -8907,21 +8937,24 @@ }, "mkdirp": { "version": "0.5.1", - "bundled": true, - "optional": true, + "resolved": false, + "integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=", + "dev": true, "requires": { "minimist": "0.0.8" } }, "ms": { "version": "2.0.0", - "bundled": true, + "resolved": false, + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", "dev": true, "optional": true }, "needle": { "version": "2.2.0", - "bundled": true, + "resolved": false, + "integrity": "sha512-eFagy6c+TYayorXw/qtAdSvaUpEbBsDwDyxYFgLZ0lTojfH7K+OdBqAF7TAFwDokJaGpubpSGG0wO3iC0XPi8w==", "dev": true, "optional": true, "requires": { @@ -8932,7 +8965,8 @@ }, "node-pre-gyp": { "version": "0.10.0", - "bundled": true, + "resolved": false, + "integrity": "sha512-G7kEonQLRbcA/mOoFoxvlMrw6Q6dPf92+t/l0DFSMuSlDoWaI9JWIyPwK0jyE1bph//CUEL65/Fz1m2vJbmjQQ==", "dev": true, "optional": true, "requires": { @@ -8950,7 +8984,8 @@ }, "nopt": { "version": "4.0.1", - "bundled": true, + "resolved": false, + "integrity": "sha1-0NRoWv1UFRk8jHUFYC0NF81kR00=", "dev": true, "optional": true, "requires": { @@ -8960,13 +8995,15 @@ }, "npm-bundled": { "version": "1.0.3", - "bundled": true, + "resolved": false, + "integrity": "sha512-ByQ3oJ/5ETLyglU2+8dBObvhfWXX8dtPZDMePCahptliFX2iIuhyEszyFk401PZUNQH20vvdW5MLjJxkwU80Ow==", "dev": true, "optional": true }, "npm-packlist": { "version": "1.1.10", - "bundled": true, + "resolved": false, + "integrity": "sha512-AQC0Dyhzn4EiYEfIUjCdMl0JJ61I2ER9ukf/sLxJUcZHfo+VyEfz2rMJgLZSS1v30OxPQe1cN0LZA1xbcaVfWA==", "dev": true, "optional": true, "requires": { @@ -8976,7 +9013,8 @@ }, "npmlog": { "version": "4.1.2", - "bundled": true, + "resolved": false, + "integrity": "sha512-2uUqazuKlTaSI/dC8AzicUck7+IrEaOnN/e0jd3Xtt1KcGpwx30v50mL7oPyr/h9bL3E4aZccVwpwP+5W9Vjkg==", "dev": true, "optional": true, "requires": { @@ -8988,38 +9026,44 @@ }, "number-is-nan": { "version": "1.0.1", - "bundled": true, - "optional": true + "resolved": false, + "integrity": "sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0=", + "dev": true }, "object-assign": { "version": "4.1.1", - "bundled": true, + "resolved": false, + "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=", "dev": true, "optional": true }, "once": { "version": "1.4.0", - "bundled": true, - "optional": true, + "resolved": false, + "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", + "dev": true, "requires": { "wrappy": "1" } }, "os-homedir": { "version": "1.0.2", - "bundled": true, + "resolved": false, + "integrity": "sha1-/7xJiDNuDoM94MFox+8VISGqf7M=", "dev": true, "optional": true }, "os-tmpdir": { "version": "1.0.2", - "bundled": true, + "resolved": false, + "integrity": "sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ=", "dev": true, "optional": true }, "osenv": { "version": "0.1.5", - "bundled": true, + "resolved": false, + "integrity": "sha512-0CWcCECdMVc2Rw3U5w9ZjqX6ga6ubk1xDVKxtBQPK7wis/0F2r9T6k4ydGYhecl7YUBxBVxhL5oisPsNxAPe2g==", "dev": true, "optional": true, "requires": { @@ -9029,19 +9073,22 @@ }, "path-is-absolute": { "version": "1.0.1", - "bundled": true, + "resolved": false, + "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=", "dev": true, "optional": true }, "process-nextick-args": { "version": "2.0.0", - "bundled": true, + "resolved": false, + "integrity": "sha512-MtEC1TqN0EU5nephaJ4rAtThHtC86dNN9qCuEhtshvpVBkAW5ZO7BASN9REnF9eoXGcRub+pFuKEpOHE+HbEMw==", "dev": true, "optional": true }, "rc": { "version": "1.2.7", - "bundled": true, + "resolved": false, + "integrity": "sha512-LdLD8xD4zzLsAT5xyushXDNscEjB7+2ulnl8+r1pnESlYtlJtVSoCMBGr30eDRJ3+2Gq89jK9P9e4tCEH1+ywA==", "dev": true, "optional": true, "requires": { @@ -9053,7 +9100,8 @@ "dependencies": { "minimist": { "version": "1.2.0", - "bundled": true, + "resolved": false, + "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=", "dev": true, "optional": true } @@ -9061,7 +9109,8 @@ }, "readable-stream": { "version": "2.3.6", - "bundled": true, + "resolved": false, + "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==", "dev": true, "optional": true, "requires": { @@ -9076,7 +9125,8 @@ }, "rimraf": { "version": "2.6.2", - "bundled": true, + "resolved": false, + "integrity": "sha512-lreewLK/BlghmxtfH36YYVg1i8IAce4TI7oao75I1g245+6BctqTVQiBP3YUJ9C6DQOXJmkYR9X9fCLtCOJc5w==", "dev": true, "optional": true, "requires": { @@ -9085,43 +9135,50 @@ }, "safe-buffer": { "version": "5.1.1", - "bundled": true, - "optional": true + "resolved": false, + "integrity": "sha512-kKvNJn6Mm93gAczWVJg7wH+wGYWNrDHdWvpUmHyEsgCtIwwo3bqPtV4tR5tuPaUhTOo/kvhVwd8XwwOllGYkbg==", + "dev": true }, "safer-buffer": { "version": "2.1.2", - "bundled": true, + "resolved": false, + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", "dev": true, "optional": true }, "sax": { "version": "1.2.4", - "bundled": true, + "resolved": false, + "integrity": "sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw==", "dev": true, "optional": true }, "semver": { "version": "5.5.0", - "bundled": true, + "resolved": false, + "integrity": "sha512-4SJ3dm0WAwWy/NVeioZh5AntkdJoWKxHxcmyP622fOkgHa4z3R0TdBJICINyaSDE6uNwVc8gZr+ZinwZAH4xIA==", "dev": true, "optional": true }, "set-blocking": { "version": "2.0.0", - "bundled": true, + "resolved": false, + "integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc=", "dev": true, "optional": true }, "signal-exit": { "version": "3.0.2", - "bundled": true, + "resolved": false, + "integrity": "sha1-tf3AjxKH6hF4Yo5BXiUTK3NkbG0=", "dev": true, "optional": true }, "string-width": { "version": "1.0.2", - "bundled": true, - "optional": true, + "resolved": false, + "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=", + "dev": true, "requires": { "code-point-at": "^1.0.0", "is-fullwidth-code-point": "^1.0.0", @@ -9130,7 +9187,8 @@ }, "string_decoder": { "version": "1.1.1", - "bundled": true, + "resolved": false, + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", "dev": true, "optional": true, "requires": { @@ -9139,21 +9197,24 @@ }, "strip-ansi": { "version": "3.0.1", - "bundled": true, - "optional": true, + "resolved": false, + "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", + "dev": true, "requires": { "ansi-regex": "^2.0.0" } }, "strip-json-comments": { "version": "2.0.1", - "bundled": true, + "resolved": false, + "integrity": "sha1-PFMZQukIwml8DsNEhYwobHygpgo=", "dev": true, "optional": true }, "tar": { "version": "4.4.1", - "bundled": true, + "resolved": false, + "integrity": "sha512-O+v1r9yN4tOsvl90p5HAP4AEqbYhx4036AGMm075fH9F8Qwi3oJ+v4u50FkT/KkvywNGtwkk0zRI+8eYm1X/xg==", "dev": true, "optional": true, "requires": { @@ -9168,13 +9229,15 @@ }, "util-deprecate": { "version": "1.0.2", - "bundled": true, + "resolved": false, + "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=", "dev": true, "optional": true }, "wide-align": { "version": "1.1.2", - "bundled": true, + "resolved": false, + "integrity": "sha512-ijDLlyQ7s6x1JgCLur53osjm/UXUYD9+0PbYKrBsYisYXzCxN+HC3mYDNy/dWdmf3AwqwU3CXwDCvsNgGK1S0w==", "dev": true, "optional": true, "requires": { @@ -9183,13 +9246,15 @@ }, "wrappy": { "version": "1.0.2", - "bundled": true, - "optional": true + "resolved": false, + "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=", + "dev": true }, "yallist": { "version": "3.0.2", - "bundled": true, - "optional": true + "resolved": false, + "integrity": "sha1-hFK0u36Dx8GI2AQcGoN8dz1ti7k=", + "dev": true } } }, @@ -9298,7 +9363,7 @@ }, "gettext-parser": { "version": "1.1.0", - "resolved": "http://registry.npmjs.org/gettext-parser/-/gettext-parser-1.1.0.tgz", + "resolved": "https://registry.npmjs.org/gettext-parser/-/gettext-parser-1.1.0.tgz", "integrity": "sha1-LFpmONiTk0ubVQN9CtgstwBLJnk=", "dev": true, "requires": { @@ -9390,7 +9455,7 @@ }, "globby": { "version": "6.1.0", - "resolved": "http://registry.npmjs.org/globby/-/globby-6.1.0.tgz", + "resolved": "https://registry.npmjs.org/globby/-/globby-6.1.0.tgz", "integrity": "sha1-9abXDoOV4hyFj7BInWTfAkJNUGw=", "dev": true, "requires": { @@ -9479,7 +9544,7 @@ }, "minimist": { "version": "0.0.10", - "resolved": "http://registry.npmjs.org/minimist/-/minimist-0.0.10.tgz", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.10.tgz", "integrity": "sha1-3j+YVD2/lggr5IrRoMfNqDYwHc8=", "dev": true }, @@ -9667,7 +9732,7 @@ }, "hoist-non-react-statics": { "version": "1.2.0", - "resolved": "http://registry.npmjs.org/hoist-non-react-statics/-/hoist-non-react-statics-1.2.0.tgz", + "resolved": "https://registry.npmjs.org/hoist-non-react-statics/-/hoist-non-react-statics-1.2.0.tgz", "integrity": "sha1-qkSM8JhtVcxAdzsXF0t90GbLfPs=" }, "homedir-polyfill": { @@ -9771,7 +9836,7 @@ }, "http-errors": { "version": "1.6.3", - "resolved": "http://registry.npmjs.org/http-errors/-/http-errors-1.6.3.tgz", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.6.3.tgz", "integrity": "sha1-i1VoC7S+KDoLW/TqLjhYC+HZMg0=", "dev": true, "requires": { @@ -10519,7 +10584,7 @@ }, "is-accessor-descriptor": { "version": "0.1.6", - "resolved": "http://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz", + "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz", "integrity": "sha1-qeEss66Nh2cn7u84Q/igiXtcmNY=", "dev": true, "requires": { @@ -10607,7 +10672,7 @@ }, "is-data-descriptor": { "version": "0.1.4", - "resolved": "http://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz", + "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz", "integrity": "sha1-C17mSDiOLIYCgueT8YVv7D8wG1Y=", "dev": true, "requires": { @@ -10712,7 +10777,7 @@ }, "is-obj": { "version": "1.0.1", - "resolved": "http://registry.npmjs.org/is-obj/-/is-obj-1.0.1.tgz", + "resolved": "https://registry.npmjs.org/is-obj/-/is-obj-1.0.1.tgz", "integrity": "sha1-PkcprB9f3gJc19g6iW2rn09n2w8=", "dev": true }, @@ -12047,13 +12112,6 @@ "stream-to-buffer": "^0.1.0", "tinycolor2": "^1.1.2", "url-regex": "^3.0.0" - }, - "dependencies": { - "bignumber.js": { - "version": "2.4.0", - "resolved": "https://registry.npmjs.org/bignumber.js/-/bignumber.js-2.4.0.tgz", - "integrity": "sha1-g4qZLan51zfg9LLbC+YrsJ3Qxeg=" - } } }, "jpeg-js": { @@ -12585,7 +12643,7 @@ }, "magic-string": { "version": "0.22.5", - "resolved": "http://registry.npmjs.org/magic-string/-/magic-string-0.22.5.tgz", + "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.22.5.tgz", "integrity": "sha512-oreip9rJZkzvA8Qzk9HFs8fZGF/u7H/gtrE8EN6RjKJ9kh2HlC+yQ2QezifqTZfGyiuAV0dRv5a+y/8gBb1m9w==", "requires": { "vlq": "^0.2.2" @@ -12708,7 +12766,7 @@ }, "minimist": { "version": "0.0.8", - "resolved": "http://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz", "integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=" }, "quickselect": { @@ -12766,7 +12824,7 @@ }, "mathjs": { "version": "3.20.2", - "resolved": "http://registry.npmjs.org/mathjs/-/mathjs-3.20.2.tgz", + "resolved": "https://registry.npmjs.org/mathjs/-/mathjs-3.20.2.tgz", "integrity": "sha512-3f6/+uf1cUtIz1rYFz775wekl/UEDSQ3mU6xdxW7qzpvvhc2v28i3UtLsGTRB+u8OqDWoSX6Dz8gehaGFs6tCA==", "requires": { "complex.js": "2.0.4", @@ -12816,7 +12874,7 @@ }, "media-typer": { "version": "0.3.0", - "resolved": "http://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", + "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", "integrity": "sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g=", "dev": true }, @@ -13016,7 +13074,7 @@ }, "minimist": { "version": "1.2.0", - "resolved": "http://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=" }, "mississippi": { @@ -13068,7 +13126,7 @@ }, "mkdirp": { "version": "0.5.1", - "resolved": "http://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz", "integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=", "requires": { "minimist": "0.0.8" @@ -13076,7 +13134,7 @@ "dependencies": { "minimist": { "version": "0.0.8", - "resolved": "http://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz", "integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=" } } @@ -13743,7 +13801,7 @@ }, "os-tmpdir": { "version": "1.0.2", - "resolved": "http://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz", + "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz", "integrity": "sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ=", "dev": true }, @@ -13930,7 +13988,7 @@ }, "path-browserify": { "version": "0.0.0", - "resolved": "http://registry.npmjs.org/path-browserify/-/path-browserify-0.0.0.tgz", + "resolved": "https://registry.npmjs.org/path-browserify/-/path-browserify-0.0.0.tgz", "integrity": "sha1-oLhwcpquIUAFt9UDLsLLuw+0RRo=", "dev": true }, @@ -13948,7 +14006,7 @@ }, "path-is-absolute": { "version": "1.0.1", - "resolved": "http://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=" }, "path-is-inside": { @@ -14089,7 +14147,7 @@ }, "po2json": { "version": "0.4.5", - "resolved": "http://registry.npmjs.org/po2json/-/po2json-0.4.5.tgz", + "resolved": "https://registry.npmjs.org/po2json/-/po2json-0.4.5.tgz", "integrity": "sha1-R7spUtoy1Yob4vJWpZjuvAt0URg=", "dev": true, "requires": { @@ -14151,7 +14209,7 @@ "dependencies": { "async": { "version": "1.5.2", - "resolved": "http://registry.npmjs.org/async/-/async-1.5.2.tgz", + "resolved": "https://registry.npmjs.org/async/-/async-1.5.2.tgz", "integrity": "sha1-7GphrlZIDAw8skHJVhjiCJL5Zyo=", "dev": true } @@ -16319,7 +16377,7 @@ }, "react-ace": { "version": "5.10.0", - "resolved": "http://registry.npmjs.org/react-ace/-/react-ace-5.10.0.tgz", + "resolved": "https://registry.npmjs.org/react-ace/-/react-ace-5.10.0.tgz", "integrity": "sha512-aEK/XZCowP8IXq91e2DYqOtGhabk1bbjt+fyeW0UBcIkzDzP/RX/MeJKeyW7wsZcwElACVwyy9nnwXBTqgky3A==", "requires": { "brace": "^0.11.0", @@ -16398,7 +16456,7 @@ }, "react-dnd": { "version": "2.6.0", - "resolved": "http://registry.npmjs.org/react-dnd/-/react-dnd-2.6.0.tgz", + "resolved": "https://registry.npmjs.org/react-dnd/-/react-dnd-2.6.0.tgz", "integrity": "sha1-f6JWds+CfViokSk+PBq1naACVFo=", "requires": { "disposables": "^1.0.1", @@ -16418,7 +16476,7 @@ }, "react-dnd-html5-backend": { "version": "2.6.0", - "resolved": "http://registry.npmjs.org/react-dnd-html5-backend/-/react-dnd-html5-backend-2.6.0.tgz", + "resolved": "https://registry.npmjs.org/react-dnd-html5-backend/-/react-dnd-html5-backend-2.6.0.tgz", "integrity": "sha1-WQzRzKeEQbsnTt1XH+9MCxbdz44=", "requires": { "lodash": "^4.2.0" @@ -16866,7 +16924,7 @@ }, "readable-stream": { "version": "2.3.6", - "resolved": "http://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz", "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==", "requires": { "core-util-is": "~1.0.0", @@ -17217,7 +17275,7 @@ }, "reduce-css-calc": { "version": "1.3.0", - "resolved": "http://registry.npmjs.org/reduce-css-calc/-/reduce-css-calc-1.3.0.tgz", + "resolved": "https://registry.npmjs.org/reduce-css-calc/-/reduce-css-calc-1.3.0.tgz", "integrity": "sha1-dHyRTgSWFKTJz7umKYca0dKSdxY=", "requires": { "balanced-match": "^0.4.2", @@ -17340,7 +17398,7 @@ }, "regexpp": { "version": "1.1.0", - "resolved": "http://registry.npmjs.org/regexpp/-/regexpp-1.1.0.tgz", + "resolved": "https://registry.npmjs.org/regexpp/-/regexpp-1.1.0.tgz", "integrity": "sha512-LOPw8FpgdQF9etWMaAfG/WRthIdXJGYp4mJ2Jgn/2lpkbod9jPn0t9UqN7AxBOKNfzRbYyVfgc7Vk4t/MpnXgw==", "dev": true }, @@ -17372,7 +17430,7 @@ "dependencies": { "jsesc": { "version": "0.5.0", - "resolved": "http://registry.npmjs.org/jsesc/-/jsesc-0.5.0.tgz", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-0.5.0.tgz", "integrity": "sha1-597mbjXW/Bb3EP6R1c9p9w8IkR0=", "dev": true } @@ -17488,7 +17546,7 @@ }, "require-uncached": { "version": "1.0.3", - "resolved": "http://registry.npmjs.org/require-uncached/-/require-uncached-1.0.3.tgz", + "resolved": "https://registry.npmjs.org/require-uncached/-/require-uncached-1.0.3.tgz", "integrity": "sha1-Tg1W1slmL9MeQwEcS5WqSZVUIdM=", "dev": true, "requires": { @@ -17571,7 +17629,7 @@ }, "rgba-regex": { "version": "1.0.0", - "resolved": "http://registry.npmjs.org/rgba-regex/-/rgba-regex-1.0.0.tgz", + "resolved": "https://registry.npmjs.org/rgba-regex/-/rgba-regex-1.0.0.tgz", "integrity": "sha1-QzdOLiyglosO8VI0YLfXMP8i7rM=", "dev": true }, @@ -17655,7 +17713,7 @@ }, "safe-regex": { "version": "1.1.0", - "resolved": "http://registry.npmjs.org/safe-regex/-/safe-regex-1.1.0.tgz", + "resolved": "https://registry.npmjs.org/safe-regex/-/safe-regex-1.1.0.tgz", "integrity": "sha1-QKNmnzsHfR6UPURinhV91IAjvy4=", "dev": true, "requires": { @@ -17833,7 +17891,7 @@ }, "sha.js": { "version": "2.4.11", - "resolved": "http://registry.npmjs.org/sha.js/-/sha.js-2.4.11.tgz", + "resolved": "https://registry.npmjs.org/sha.js/-/sha.js-2.4.11.tgz", "integrity": "sha512-QMEp5B7cftE7APOjk5Y6xgrbWu+WkLVQwk8JNjZ8nKRciZaByEW6MubieAiToS7+dwvrjGhH8jRXz3MVd0AYqQ==", "dev": true, "requires": { @@ -17868,7 +17926,7 @@ }, "iconv-lite": { "version": "0.2.11", - "resolved": "http://registry.npmjs.org/iconv-lite/-/iconv-lite-0.2.11.tgz", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.2.11.tgz", "integrity": "sha1-HOYKOleGSiktEyH/RgnKS7llrcg=" } } @@ -17888,7 +17946,7 @@ "dependencies": { "minimist": { "version": "0.0.5", - "resolved": "http://registry.npmjs.org/minimist/-/minimist-0.0.5.tgz", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.5.tgz", "integrity": "sha1-16oye87PUY+RBqxrjwA/o7zqhWY=" } } @@ -17947,7 +18005,7 @@ }, "sinon": { "version": "4.5.0", - "resolved": "http://registry.npmjs.org/sinon/-/sinon-4.5.0.tgz", + "resolved": "https://registry.npmjs.org/sinon/-/sinon-4.5.0.tgz", "integrity": "sha512-trdx+mB0VBBgoYucy6a9L7/jfQOmvGeaKZT4OOJ+lPAtI8623xyGr8wLiE4eojzBS8G9yXbhx42GHUOVLr4X2w==", "dev": true, "requires": { @@ -18339,7 +18397,7 @@ }, "split": { "version": "0.2.10", - "resolved": "http://registry.npmjs.org/split/-/split-0.2.10.tgz", + "resolved": "https://registry.npmjs.org/split/-/split-0.2.10.tgz", "integrity": "sha1-Zwl8YB1pfOE2j0GPBs0gHPBSGlc=", "requires": { "through": "2" @@ -18382,7 +18440,7 @@ }, "sprintf-js": { "version": "1.0.3", - "resolved": "http://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", + "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", "integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=", "dev": true }, @@ -18634,7 +18692,7 @@ }, "string_decoder": { "version": "1.1.1", - "resolved": "http://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", "requires": { "safe-buffer": "~5.1.0" @@ -18642,7 +18700,7 @@ }, "strip-ansi": { "version": "3.0.1", - "resolved": "http://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", "dev": true, "requires": { @@ -18657,7 +18715,7 @@ }, "strip-eof": { "version": "1.0.0", - "resolved": "http://registry.npmjs.org/strip-eof/-/strip-eof-1.0.0.tgz", + "resolved": "https://registry.npmjs.org/strip-eof/-/strip-eof-1.0.0.tgz", "integrity": "sha1-u0P/VZim6wXYm1n80SnJgzE2Br8=", "dev": true }, @@ -18902,7 +18960,7 @@ }, "fast-deep-equal": { "version": "1.1.0", - "resolved": "http://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-1.1.0.tgz", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-1.1.0.tgz", "integrity": "sha1-wFNHeBfIa1HaqFPIHgWbcz0CNhQ=", "dev": true }, @@ -18959,7 +19017,7 @@ }, "tapable": { "version": "0.1.10", - "resolved": "http://registry.npmjs.org/tapable/-/tapable-0.1.10.tgz", + "resolved": "https://registry.npmjs.org/tapable/-/tapable-0.1.10.tgz", "integrity": "sha1-KcNXB8K3DlDQdIK10gLo7URtr9Q=", "dev": true }, @@ -19147,7 +19205,7 @@ }, "through": { "version": "2.3.8", - "resolved": "http://registry.npmjs.org/through/-/through-2.3.8.tgz", + "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz", "integrity": "sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU=" }, "through2": { @@ -19843,7 +19901,7 @@ }, "tty-browserify": { "version": "0.0.0", - "resolved": "http://registry.npmjs.org/tty-browserify/-/tty-browserify-0.0.0.tgz", + "resolved": "https://registry.npmjs.org/tty-browserify/-/tty-browserify-0.0.0.tgz", "integrity": "sha1-oVe6QC2iTpv5V/mqadUk7tQpAaY=", "dev": true }, @@ -20376,7 +20434,7 @@ }, "vm-browserify": { "version": "0.0.4", - "resolved": "http://registry.npmjs.org/vm-browserify/-/vm-browserify-0.0.4.tgz", + "resolved": "https://registry.npmjs.org/vm-browserify/-/vm-browserify-0.0.4.tgz", "integrity": "sha1-XX6kW7755Kb/ZflUOOCofDV9WnM=", "dev": true, "requires": { @@ -21958,7 +22016,7 @@ }, "wrap-ansi": { "version": "2.1.0", - "resolved": "http://registry.npmjs.org/wrap-ansi/-/wrap-ansi-2.1.0.tgz", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-2.1.0.tgz", "integrity": "sha1-2Pw9KE3QV5T+hJc8rs3Rz4JP3YU=", "dev": true, "requires": { @@ -22043,7 +22101,7 @@ }, "xmlbuilder": { "version": "9.0.7", - "resolved": "http://registry.npmjs.org/xmlbuilder/-/xmlbuilder-9.0.7.tgz", + "resolved": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-9.0.7.tgz", "integrity": "sha1-Ey7mPS7FVlxVfiD0wi35rKaGsQ0=" }, "xregexp": { diff --git a/superset/assets/package.json b/superset/assets/package.json index d5a75d45d..84c15aeb4 100644 --- a/superset/assets/package.json +++ b/superset/assets/package.json @@ -50,7 +50,7 @@ "@data-ui/sparkline": "^0.0.54", "@superset-ui/chart": "^0.10.2", "@superset-ui/color": "^0.10.9", - "@superset-ui/connection": "^0.10.2", + "@superset-ui/connection": "^0.11.0", "@superset-ui/core": "^0.10.0", "@superset-ui/dimension": "^0.10.0", "@superset-ui/legacy-plugin-chart-calendar": "^0.10.0", @@ -82,7 +82,6 @@ "@superset-ui/translation": "^0.10.0", "@vx/responsive": "0.0.172", "abortcontroller-polyfill": "^1.1.9", - "bignumber.js": "^8.1.1", "bootstrap": "^3.3.6", "bootstrap-slider": "^10.0.0", "brace": "^0.11.1", diff --git a/superset/assets/spec/javascripts/chart/transformBigNumber_spec.js b/superset/assets/spec/javascripts/chart/transformBigNumber_spec.js deleted file mode 100644 index 1500a4243..000000000 --- a/superset/assets/spec/javascripts/chart/transformBigNumber_spec.js +++ /dev/null @@ -1,53 +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 BigNumber from 'bignumber.js'; -import transform from 'src/chart/transformBigNumber'; - -describe('transformBigNumber', () => { - it('should transform BigNumber on its own', () => { - expect(transform(new BigNumber(123.456))).toBe(123.456); - }); - - it('should transform BigNumber in objects', () => { - expect(transform({ - foo: new BigNumber(123), - bar: 456, - baz: null, - })).toEqual({ foo: 123, bar: 456, baz: null }); - }); - - it('should transform BigNumber in arrays', () => { - expect(transform([ - { foo: new BigNumber(123) }, - { bar: 456 }, - ])).toEqual([{ foo: 123 }, { bar: 456 }]); - }); - - it('should transform BigNumber in nested structures', () => { - expect(transform([{ - x: new BigNumber(123), - y: [{ foo: new BigNumber(456) }, { bar: 'str' }], - z: { some: [new BigNumber(789)] }, - }])).toEqual([{ - x: 123, - y: [{ foo: 456 }, { bar: 'str' }], - z: { some: [789] }, - }]); - }); -}); diff --git a/superset/assets/spec/javascripts/visualizations/deckgl/utils_spec.js b/superset/assets/spec/javascripts/visualizations/deckgl/utils_spec.js index bcc328897..bd256a458 100644 --- a/superset/assets/spec/javascripts/visualizations/deckgl/utils_spec.js +++ b/superset/assets/spec/javascripts/visualizations/deckgl/utils_spec.js @@ -30,7 +30,7 @@ describe('getBreakPoints', () => { }); it('returns sorted break points', () => { - const fd = { breakPoints: ['0', '10', '100', '50', '1000'] }; + const fd = { break_points: ['0', '10', '100', '50', '1000'] }; const result = getBreakPoints(fd, [], metricAccessor); const expected = ['0', '10', '50', '100', '1000']; expect(result).toEqual(expected); @@ -45,7 +45,7 @@ describe('getBreakPoints', () => { }); it('formats number with proper precision', () => { - const fd = { metric: 'count', numBuckets: 2 }; + const fd = { metric: 'count', num_buckets: 2 }; const features = [0, 1 / 3, 2 / 3, 1].map(count => ({ count })); const result = getBreakPoints(fd, features, metricAccessor); const expected = ['0.0', '0.5', '1.0']; @@ -53,7 +53,7 @@ describe('getBreakPoints', () => { }); it('works with a zero range', () => { - const fd = { metric: 'count', numBuckets: 1 }; + const fd = { metric: 'count', num_buckets: 1 }; const features = [1, 1, 1].map(count => ({ count })); const result = getBreakPoints(fd, features, metricAccessor); const expected = ['1', '1']; @@ -69,7 +69,7 @@ describe('getBreakPointColorScaler', () => { it('returns linear color scaler if there are no break points', () => { const fd = { metric: 'count', - linearColorScheme: ['#000000', '#ffffff'], + linear_color_scheme: ['#000000', '#ffffff'], opacity: 100, }; const features = [10, 20, 30].map(count => ({ count })); @@ -82,8 +82,8 @@ describe('getBreakPointColorScaler', () => { it('returns bucketing scaler if there are break points', () => { const fd = { metric: 'count', - linearColorScheme: ['#000000', '#ffffff'], - breakPoints: ['0', '1', '10'], + linear_color_scheme: ['#000000', '#ffffff'], + break_points: ['0', '1', '10'], opacity: 100, }; const features = []; @@ -97,8 +97,8 @@ describe('getBreakPointColorScaler', () => { it('mask values outside the break points', () => { const fd = { metric: 'count', - linearColorScheme: ['#000000', '#ffffff'], - breakPoints: ['0', '1', '10'], + linear_color_scheme: ['#000000', '#ffffff'], + break_points: ['0', '1', '10'], opacity: 100, }; const features = []; @@ -116,8 +116,8 @@ describe('getBuckets', () => { it('computes buckets for break points', () => { const fd = { metric: 'count', - linearColorScheme: ['#000000', '#ffffff'], - breakPoints: ['0', '1', '10'], + linear_color_scheme: ['#000000', '#ffffff'], + break_points: ['0', '1', '10'], opacity: 100, }; const features = []; diff --git a/superset/assets/src/SqlLab/actions/sqlLab.js b/superset/assets/src/SqlLab/actions/sqlLab.js index 677373daf..b3d61a8ee 100644 --- a/superset/assets/src/SqlLab/actions/sqlLab.js +++ b/superset/assets/src/SqlLab/actions/sqlLab.js @@ -17,6 +17,7 @@ * under the License. */ import shortid from 'shortid'; +import JSONbig from 'json-bigint'; import { t } from '@superset-ui/translation'; import { SupersetClient } from '@superset-ui/connection'; @@ -128,9 +129,11 @@ export function fetchQueryResults(query) { return SupersetClient.get({ endpoint: `/superset/results/${query.resultsKey}/`, + parseMethod: 'text', }) - .then(({ json = {} }) => { - dispatch(querySuccess(query, json)); + .then(({ text = '{}' }) => { + const bigIntJson = JSONbig.parse(text); + dispatch(querySuccess(query, bigIntJson)); }) .catch(response => getClientErrorObject(response).then((error) => { @@ -164,10 +167,12 @@ export function runQuery(query) { endpoint: `/superset/sql_json/${window.location.search}`, postPayload, stringify: false, + parseMethod: 'text', }) - .then(({ json }) => { + .then(({ text = '{}' }) => { if (!query.runAsync) { - dispatch(querySuccess(query, json)); + const bigIntJson = JSONbig.parse(text); + dispatch(querySuccess(query, bigIntJson)); } }) .catch(response => diff --git a/superset/assets/src/chart/ChartRenderer.jsx b/superset/assets/src/chart/ChartRenderer.jsx index e46f941ff..9f366f5cc 100644 --- a/superset/assets/src/chart/ChartRenderer.jsx +++ b/superset/assets/src/chart/ChartRenderer.jsx @@ -23,7 +23,6 @@ import React from 'react'; import { ChartProps, SuperChart } from '@superset-ui/chart'; import { Tooltip } from 'react-bootstrap'; import { Logger, LOG_ACTIONS_RENDER_CHART } from '../logger/LogUtils'; -import transformBigNumber from './transformBigNumber'; const propTypes = { annotationData: PropTypes.object, @@ -68,7 +67,6 @@ class ChartRenderer extends React.Component { this.handleAddFilter = this.handleAddFilter.bind(this); this.handleRenderSuccess = this.handleRenderSuccess.bind(this); this.handleRenderFailure = this.handleRenderFailure.bind(this); - this.preTransformProps = this.preTransformProps.bind(this); } shouldComponentUpdate(nextProps, nextState) { @@ -167,18 +165,6 @@ class ChartRenderer extends React.Component { } } - preTransformProps(chartProps) { - const payload = chartProps.payload; - const data = transformBigNumber(payload.data); - return new ChartProps({ - ...chartProps, - payload: { - ...payload, - data, - }, - }); - } - renderTooltip() { const { tooltip } = this.state; if (tooltip && tooltip.content) { @@ -224,7 +210,6 @@ class ChartRenderer extends React.Component { className={`${snakeCase(vizType)}`} chartType={vizType} chartProps={skipChartRendering ? null : this.prepareChartProps()} - preTransformProps={this.preTransformProps} onRenderSuccess={this.handleRenderSuccess} onRenderFailure={this.handleRenderFailure} /> diff --git a/superset/assets/src/chart/transformBigNumber.js b/superset/assets/src/chart/transformBigNumber.js deleted file mode 100644 index dffa02a7f..000000000 --- a/superset/assets/src/chart/transformBigNumber.js +++ /dev/null @@ -1,45 +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. - */ -// This method transforms any BigNumber object in the given payload to its -// 64-bit float representation. It is a temporary fix so charts receive -// floats instead of BigNumber instances in their props to properly render. -import BigNumber from 'bignumber.js'; - -export default function transform(payload) { - if (!payload) { - return payload; - } else if (BigNumber.isBigNumber(payload)) { - return payload.toNumber(); - } else if (payload.constructor === Object) { - for (const key in payload) { - if (payload.hasOwnProperty(key)) { - // Modify in place to prevent creating large payloads - // eslint-disable-next-line no-param-reassign - payload[key] = transform(payload[key]); - } - } - } else if (payload.constructor === Array) { - payload.forEach((elem, idx) => { - // Modify in place to prevent creating large payloads - // eslint-disable-next-line no-param-reassign - payload[idx] = transform(elem); - }); - } - return payload; -} diff --git a/superset/assets/src/visualizations/deckgl/CategoricalDeckGLContainer.jsx b/superset/assets/src/visualizations/deckgl/CategoricalDeckGLContainer.jsx index 3765f7d49..e1933a4b1 100644 --- a/superset/assets/src/visualizations/deckgl/CategoricalDeckGLContainer.jsx +++ b/superset/assets/src/visualizations/deckgl/CategoricalDeckGLContainer.jsx @@ -31,9 +31,9 @@ import { fitViewport } from './layers/common'; const { getScale } = CategoricalColorNamespace; function getCategories(fd, data) { - const c = fd.colorPicker || { r: 0, g: 0, b: 0, a: 1 }; + const c = fd.color_picker || { r: 0, g: 0, b: 0, a: 1 }; const fixedColor = [c.r, c.g, c.b, 255 * c.a]; - const colorFn = getScale(fd.colorScheme); + const colorFn = getScale(fd.color_scheme); const categories = {}; data.forEach((d) => { if (d.cat_color != null && !categories.hasOwnProperty(d.cat_color)) { @@ -108,7 +108,7 @@ export default class CategoricalDeckGLContainer extends React.PureComponent { // the granularity has to be read from the payload form_data, not the // props formData which comes from the instantaneous controls state const granularity = ( - props.payload.form_data.timeGrainSqla || + props.payload.form_data.time_grain_sqla || props.payload.form_data.granularity || 'P1D' ); @@ -154,8 +154,8 @@ export default class CategoricalDeckGLContainer extends React.PureComponent { features = this.addColor(features, fd); // Apply user defined data mutator if defined - if (fd.jsDataMutator) { - const jsFnMutator = sandboxedEval(fd.jsDataMutator); + if (fd.js_data_mutator) { + const jsFnMutator = sandboxedEval(fd.js_data_mutator); features = jsFnMutator(features); } @@ -180,8 +180,8 @@ export default class CategoricalDeckGLContainer extends React.PureComponent { return [getLayer(fd, filteredPayload, onAddFilter, setTooltip)]; } addColor(data, fd) { - const c = fd.colorPicker || { r: 0, g: 0, b: 0, a: 1 }; - const colorFn = getScale(fd.colorScheme); + const c = fd.color_picker || { r: 0, g: 0, b: 0, a: 1 }; + const colorFn = getScale(fd.color_scheme); return data.map((d) => { let color; if (fd.dimension) { @@ -229,14 +229,14 @@ export default class CategoricalDeckGLContainer extends React.PureComponent { viewport={this.state.viewport} onViewportChange={this.onViewportChange} mapboxApiAccessToken={this.props.mapboxApiKey} - mapStyle={this.props.formData.mapboxStyle} + mapStyle={this.props.formData.mapbox_style} setControlValue={this.props.setControlValue} >
diff --git a/superset/assets/src/visualizations/deckgl/Multi/Multi.jsx b/superset/assets/src/visualizations/deckgl/Multi/Multi.jsx index 792b94ad0..e9da1f772 100644 --- a/superset/assets/src/visualizations/deckgl/Multi/Multi.jsx +++ b/superset/assets/src/visualizations/deckgl/Multi/Multi.jsx @@ -56,7 +56,7 @@ class DeckMulti extends React.PureComponent { const filters = [ ...(subslice.form_data.filters || []), ...(formData.filters || []), - ...(formData.extraFilters || []), + ...(formData.extra_filters || []), ]; const subsliceCopy = { ...subslice, @@ -70,7 +70,7 @@ class DeckMulti extends React.PureComponent { endpoint: getExploreLongUrl(subsliceCopy.form_data, 'json'), }) .then(({ json }) => { - const layer = layerGenerators[subsliceCopy.form_data.vizType]( + const layer = layerGenerators[subsliceCopy.form_data.viz_type]( subsliceCopy.form_data, json, ); @@ -96,7 +96,7 @@ class DeckMulti extends React.PureComponent { mapboxApiAccessToken={payload.data.mapboxApiKey} viewport={viewport} layers={layers} - mapStyle={formData.mapboxStyle} + mapStyle={formData.mapbox_style} setControlValue={setControlValue} /> ); diff --git a/superset/assets/src/visualizations/deckgl/factory.jsx b/superset/assets/src/visualizations/deckgl/factory.jsx index 348bdeeb7..1ad8bd373 100644 --- a/superset/assets/src/visualizations/deckgl/factory.jsx +++ b/superset/assets/src/visualizations/deckgl/factory.jsx @@ -90,7 +90,7 @@ export function createDeckGLComponent(getLayer, getPoints) { mapboxApiAccessToken={payload.data.mapboxApiKey} viewport={viewport} layers={[layer]} - mapStyle={formData.mapboxStyle} + mapStyle={formData.mapbox_style} setControlValue={setControlValue} onViewportChange={this.onViewportChange} />); diff --git a/superset/assets/src/visualizations/deckgl/layers/Arc/Arc.jsx b/superset/assets/src/visualizations/deckgl/layers/Arc/Arc.jsx index 75894b089..b6adbbfbf 100644 --- a/superset/assets/src/visualizations/deckgl/layers/Arc/Arc.jsx +++ b/superset/assets/src/visualizations/deckgl/layers/Arc/Arc.jsx @@ -31,14 +31,14 @@ function getPoints(data) { export function getLayer(fd, payload, onAddFilter, setTooltip) { const data = payload.data.features; - const sc = fd.colorPicker; - const tc = fd.targetColorPicker; + const sc = fd.color_picker; + const tc = fd.target_color_picker; return new ArcLayer({ - id: `path-layer-${fd.sliceId}`, + id: `path-layer-${fd.slice_id}`, data, getSourceColor: d => d.sourceColor || d.color || [sc.r, sc.g, sc.b, 255 * sc.a], getTargetColor: d => d.targetColor || d.color || [tc.r, tc.g, tc.b, 255 * tc.a], - strokeWidth: (fd.strokeWidth) ? fd.strokeWidth : 3, + strokeWidth: (fd.stroke_width) ? fd.stroke_width : 3, ...commonLayerProps(fd, setTooltip), }); } diff --git a/superset/assets/src/visualizations/deckgl/layers/Geojson/Geojson.jsx b/superset/assets/src/visualizations/deckgl/layers/Geojson/Geojson.jsx index d698a0b8a..1dd3cdb47 100644 --- a/superset/assets/src/visualizations/deckgl/layers/Geojson/Geojson.jsx +++ b/superset/assets/src/visualizations/deckgl/layers/Geojson/Geojson.jsx @@ -77,8 +77,8 @@ const recurseGeoJson = (node, propOverrides, extraProps) => { export function getLayer(formData, payload, onAddFilter, setTooltip) { const fd = formData; - const fc = fd.fillColorPicker; - const sc = fd.strokeColorPicker; + const fc = fd.fill_color_picker; + const sc = fd.stroke_color_picker; const fillColor = [fc.r, fc.g, fc.b, 255 * fc.a]; const strokeColor = [sc.r, sc.g, sc.b, 255 * sc.a]; const propOverrides = {}; @@ -93,19 +93,19 @@ export function getLayer(formData, payload, onAddFilter, setTooltip) { recurseGeoJson(payload.data, propOverrides); let jsFnMutator; - if (fd.jsDataMutator) { + if (fd.js_data_mutator) { // Applying user defined data mutator if defined - jsFnMutator = sandboxedEval(fd.jsDataMutator); + jsFnMutator = sandboxedEval(fd.js_data_mutator); features = jsFnMutator(features); } return new GeoJsonLayer({ - id: `geojson-layer-${fd.sliceId}`, + id: `geojson-layer-${fd.slice_id}`, filled: fd.filled, data: features, stroked: fd.stroked, extruded: fd.extruded, - pointRadiusScale: fd.pointRadiusScale, + pointRadiusScale: fd.point_radius_scale, ...commonLayerProps(fd, setTooltip), }); } @@ -145,7 +145,7 @@ function deckGeoJson(props) { mapboxApiAccessToken={payload.data.mapboxApiKey} viewport={viewport} layers={[layer]} - mapStyle={formData.mapboxStyle} + mapStyle={formData.mapbox_style} setControlValue={setControlValue} /> ); diff --git a/superset/assets/src/visualizations/deckgl/layers/Grid/Grid.jsx b/superset/assets/src/visualizations/deckgl/layers/Grid/Grid.jsx index 2baba1e7a..67c4bfd83 100644 --- a/superset/assets/src/visualizations/deckgl/layers/Grid/Grid.jsx +++ b/superset/assets/src/visualizations/deckgl/layers/Grid/Grid.jsx @@ -24,24 +24,24 @@ import { createDeckGLComponent } from '../../factory'; export function getLayer(formData, payload, onAddFilter, setTooltip) { const fd = formData; - const c = fd.colorPicker; + const c = fd.color_picker; let data = payload.data.features.map(d => ({ ...d, color: [c.r, c.g, c.b, 255 * c.a], })); - if (fd.jsDataMutator) { + if (fd.js_data_mutator) { // Applying user defined data mutator if defined - const jsFnMutator = sandboxedEval(fd.jsDataMutator); + const jsFnMutator = sandboxedEval(fd.js_data_mutator); data = jsFnMutator(data); } - const aggFunc = getAggFunc(fd.jsAggFunction, p => p.weight); + const aggFunc = getAggFunc(fd.js_agg_function, p => p.weight); return new GridLayer({ - id: `grid-layer-${fd.sliceId}`, + id: `grid-layer-${fd.slice_id}`, data, pickable: true, - cellSize: fd.gridSize, + cellSize: fd.grid_size, minColor: [0, 0, 0, 0], extruded: fd.extruded, maxColor: [c.r, c.g, c.b, 255 * c.a], diff --git a/superset/assets/src/visualizations/deckgl/layers/Hex/Hex.jsx b/superset/assets/src/visualizations/deckgl/layers/Hex/Hex.jsx index b17116f21..f8562254e 100644 --- a/superset/assets/src/visualizations/deckgl/layers/Hex/Hex.jsx +++ b/superset/assets/src/visualizations/deckgl/layers/Hex/Hex.jsx @@ -24,23 +24,23 @@ import { createDeckGLComponent } from '../../factory'; export function getLayer(formData, payload, onAddFilter, setTooltip) { const fd = formData; - const c = fd.colorPicker; + const c = fd.color_picker; let data = payload.data.features.map(d => ({ ...d, color: [c.r, c.g, c.b, 255 * c.a], })); - if (fd.jsDataMutator) { + if (fd.js_data_mutator) { // Applying user defined data mutator if defined - const jsFnMutator = sandboxedEval(fd.jsDataMutator); + const jsFnMutator = sandboxedEval(fd.js_data_mutator); data = jsFnMutator(data); } - const aggFunc = getAggFunc(fd.jsAggFunction, p => p.weight); + const aggFunc = getAggFunc(fd.js_agg_function, p => p.weight); return new HexagonLayer({ - id: `hex-layer-${fd.sliceId}`, + id: `hex-layer-${fd.slice_id}`, data, pickable: true, - radius: fd.gridSize, + radius: fd.grid_size, minColor: [0, 0, 0, 0], extruded: fd.extruded, maxColor: [c.r, c.g, c.b, 255 * c.a], diff --git a/superset/assets/src/visualizations/deckgl/layers/Path/Path.jsx b/superset/assets/src/visualizations/deckgl/layers/Path/Path.jsx index bd1b44979..633ccec9e 100644 --- a/superset/assets/src/visualizations/deckgl/layers/Path/Path.jsx +++ b/superset/assets/src/visualizations/deckgl/layers/Path/Path.jsx @@ -23,22 +23,22 @@ import { createDeckGLComponent } from '../../factory'; export function getLayer(formData, payload, onAddFilter, setTooltip) { const fd = formData; - const c = fd.colorPicker; + const c = fd.color_picker; const fixedColor = [c.r, c.g, c.b, 255 * c.a]; let data = payload.data.features.map(feature => ({ ...feature, path: feature.path, - width: fd.lineWidth, + width: fd.line_width, color: fixedColor, })); - if (fd.jsDataMutator) { - const jsFnMutator = sandboxedEval(fd.jsDataMutator); + if (fd.js_data_mutator) { + const jsFnMutator = sandboxedEval(fd.js_data_mutator); data = jsFnMutator(data); } return new PathLayer({ - id: `path-layer-${fd.sliceId}`, + id: `path-layer-${fd.slice_id}`, data, rounded: true, widthScale: 1, diff --git a/superset/assets/src/visualizations/deckgl/layers/Polygon/Polygon.jsx b/superset/assets/src/visualizations/deckgl/layers/Polygon/Polygon.jsx index b69bbf782..8a3c2f402 100644 --- a/superset/assets/src/visualizations/deckgl/layers/Polygon/Polygon.jsx +++ b/superset/assets/src/visualizations/deckgl/layers/Polygon/Polygon.jsx @@ -50,8 +50,8 @@ function getElevation(d, colorScaler) { export function getLayer(formData, payload, setTooltip, selected, onSelect, filters) { const fd = formData; - const fc = fd.fillColorPicker; - const sc = fd.strokeColorPicker; + const fc = fd.fill_color_picker; + const sc = fd.stroke_color_picker; let data = [...payload.data.features]; if (filters != null) { @@ -60,9 +60,9 @@ export function getLayer(formData, payload, setTooltip, selected, onSelect, filt }); } - if (fd.jsDataMutator) { + if (fd.js_data_mutator) { // Applying user defined data mutator if defined - const jsFnMutator = sandboxedEval(fd.jsDataMutator); + const jsFnMutator = sandboxedEval(fd.js_data_mutator); data = jsFnMutator(data); } @@ -76,13 +76,13 @@ export function getLayer(formData, payload, setTooltip, selected, onSelect, filt // when polygons are selected, reduce the opacity of non-selected polygons const colorScaler = (d) => { const baseColor = baseColorScaler(d); - if (selected.length > 0 && selected.indexOf(d[fd.lineColumn]) === -1) { + if (selected.length > 0 && selected.indexOf(d[fd.line_column]) === -1) { baseColor[3] /= 2; } return baseColor; }; return new PolygonLayer({ - id: `path-layer-${fd.sliceId}`, + id: `path-layer-${fd.slice_id}`, data, pickable: true, filled: fd.filled, @@ -90,7 +90,7 @@ export function getLayer(formData, payload, setTooltip, selected, onSelect, filt getPolygon: d => d.polygon, getFillColor: colorScaler, getLineColor: [sc.r, sc.g, sc.b, 255 * sc.a], - getLineWidth: fd.lineWidth, + getLineWidth: fd.line_width, extruded: fd.extruded, getElevation: d => getElevation(d, colorScaler), elevationScale: fd.multiplier, @@ -138,7 +138,7 @@ class DeckGLPolygon extends React.Component { // the granularity has to be read from the payload form_data, not the // props formData which comes from the instantaneous controls state const granularity = ( - props.payload.form_data.timeGrainSqla || + props.payload.form_data.time_grain_sqla || props.payload.form_data.granularity || 'P1D' ); @@ -177,7 +177,7 @@ class DeckGLPolygon extends React.Component { const selected = [...this.state.selected]; if (doubleClick) { selected.splice(0, selected.length, polygon); - } else if (formData.togglePolygons) { + } else if (formData.toggle_polygons) { const i = selected.indexOf(polygon); if (i === -1) { selected.push(polygon); @@ -189,8 +189,8 @@ class DeckGLPolygon extends React.Component { } this.setState({ selected, lastClick: now }); - if (formData.tableFilter) { - onAddFilter(formData.lineColumn, selected, false, true); + if (formData.table_filter) { + onAddFilter(formData.line_column, selected, false, true); } } onValuesChange(values) { @@ -249,14 +249,14 @@ class DeckGLPolygon extends React.Component { viewport={viewport} onViewportChange={this.onViewportChange} mapboxApiAccessToken={payload.data.mapboxApiKey} - mapStyle={formData.mapboxStyle} + mapStyle={formData.mapbox_style} setControlValue={setControlValue} aggregation > {formData.metric !== null && }
diff --git a/superset/assets/src/visualizations/deckgl/layers/Scatter/Scatter.jsx b/superset/assets/src/visualizations/deckgl/layers/Scatter/Scatter.jsx index ebc26c899..194a7755a 100644 --- a/superset/assets/src/visualizations/deckgl/layers/Scatter/Scatter.jsx +++ b/superset/assets/src/visualizations/deckgl/layers/Scatter/Scatter.jsx @@ -27,24 +27,24 @@ function getPoints(data) { export function getLayer(fd, payload, onAddFilter, setTooltip) { const dataWithRadius = payload.data.features.map((d) => { - let radius = unitToRadius(fd.pointUnit, d.radius) || 10; + let radius = unitToRadius(fd.point_unit, d.radius) || 10; if (fd.multiplier) { radius *= fd.multiplier; } if (d.color) { return { ...d, radius }; } - const c = fd.colorPicker || { r: 0, g: 0, b: 0, a: 1 }; + const c = fd.color_picker || { r: 0, g: 0, b: 0, a: 1 }; const color = [c.r, c.g, c.b, c.a * 255]; return { ...d, radius, color }; }); return new ScatterplotLayer({ - id: `scatter-layer-${fd.sliceId}`, + id: `scatter-layer-${fd.slice_id}`, data: dataWithRadius, fp64: true, - radiusMinPixels: fd.minRadius || null, - radiusMaxPixels: fd.maxRadius || null, + radiusMinPixels: fd.min_radius || null, + radiusMaxPixels: fd.max_radius || null, outline: false, ...commonLayerProps(fd, setTooltip), }); diff --git a/superset/assets/src/visualizations/deckgl/layers/Screengrid/Screengrid.jsx b/superset/assets/src/visualizations/deckgl/layers/Screengrid/Screengrid.jsx index cc309bbaa..a4bdede05 100644 --- a/superset/assets/src/visualizations/deckgl/layers/Screengrid/Screengrid.jsx +++ b/superset/assets/src/visualizations/deckgl/layers/Screengrid/Screengrid.jsx @@ -32,15 +32,15 @@ function getPoints(data) { export function getLayer(formData, payload, onAddFilter, setTooltip, filters) { const fd = formData; - const c = fd.colorPicker; + const c = fd.color_picker; let data = payload.data.features.map(d => ({ ...d, color: [c.r, c.g, c.b, 255 * c.a], })); - if (fd.jsDataMutator) { + if (fd.js_data_mutator) { // Applying user defined data mutator if defined - const jsFnMutator = sandboxedEval(fd.jsDataMutator); + const jsFnMutator = sandboxedEval(fd.js_data_mutator); data = jsFnMutator(data); } @@ -53,10 +53,10 @@ export function getLayer(formData, payload, onAddFilter, setTooltip, filters) { // Passing a layer creator function instead of a layer since the // layer needs to be regenerated at each render return new ScreenGridLayer({ - id: `screengrid-layer-${fd.sliceId}`, + id: `screengrid-layer-${fd.slice_id}`, data, pickable: true, - cellSizePixels: fd.gridSize, + cellSizePixels: fd.grid_size, minColor: [c.r, c.g, c.b, 0], maxColor: [c.r, c.g, c.b, 255 * c.a], outline: false, @@ -102,7 +102,7 @@ class DeckGLScreenGrid extends React.PureComponent { // the granularity has to be read from the payload form_data, not the // props formData which comes from the instantaneous controls state const granularity = ( - props.payload.form_data.timeGrainSqla || + props.payload.form_data.time_grain_sqla || props.payload.form_data.granularity || 'P1D' ); @@ -176,7 +176,7 @@ class DeckGLScreenGrid extends React.PureComponent { viewport={this.state.viewport} onViewportChange={this.onViewportChange} mapboxApiAccessToken={payload.data.mapboxApiKey} - mapStyle={formData.mapboxStyle} + mapStyle={formData.mapbox_style} setControlValue={setControlValue} aggregation /> diff --git a/superset/assets/src/visualizations/deckgl/layers/common.jsx b/superset/assets/src/visualizations/deckgl/layers/common.jsx index 2e5aeede2..b9b271640 100644 --- a/superset/assets/src/visualizations/deckgl/layers/common.jsx +++ b/superset/assets/src/visualizations/deckgl/layers/common.jsx @@ -54,13 +54,13 @@ export function commonLayerProps(formData, setTooltip, onSelect) { const fd = formData; let onHover; let tooltipContentGenerator; - if (fd.jsTooltip) { - tooltipContentGenerator = sandboxedEval(fd.jsTooltip); - } else if (fd.lineColumn && fd.metric && ['geohash', 'zipcode'].indexOf(fd.lineType) >= 0) { + if (fd.js_tooltip) { + tooltipContentGenerator = sandboxedEval(fd.js_tooltip); + } else if (fd.line_column && fd.metric && ['geohash', 'zipcode'].indexOf(fd.line_type) >= 0) { const metricLabel = fd.metric.label || fd.metric; tooltipContentGenerator = o => (
-
{fd.lineColumn}: {o.object[fd.lineColumn]}
+
{fd.line_column}: {o.object[fd.line_column]}
{fd.metric &&
{metricLabel}: {o.object[metricLabel]}
}
); @@ -79,13 +79,13 @@ export function commonLayerProps(formData, setTooltip, onSelect) { }; } let onClick; - if (fd.jsOnclickHref) { + if (fd.js_onclick_href) { onClick = (o) => { - const href = sandboxedEval(fd.jsOnclickHref)(o); + const href = sandboxedEval(fd.js_onclick_href)(o); window.open(href); }; - } else if (fd.tableFilter && onSelect !== undefined) { - onClick = o => onSelect(o.object[fd.lineColumn]); + } else if (fd.table_filter && onSelect !== undefined) { + onClick = o => onSelect(o.object[fd.line_column]); } return { onClick, diff --git a/superset/assets/src/visualizations/deckgl/utils.js b/superset/assets/src/visualizations/deckgl/utils.js index ef8ae4120..76ed54ddd 100644 --- a/superset/assets/src/visualizations/deckgl/utils.js +++ b/superset/assets/src/visualizations/deckgl/utils.js @@ -24,8 +24,8 @@ import { hexToRGB } from '../../modules/colors'; const DEFAULT_NUM_BUCKETS = 10; export function getBreakPoints({ - breakPoints: formDataBreakPoints, - numBuckets: formDataNumBuckets, + break_points: formDataBreakPoints, + num_buckets: formDataNumBuckets, }, features, accessor) { if (!features) { return []; @@ -46,15 +46,15 @@ export function getBreakPoints({ } export function getBreakPointColorScaler({ - breakPoints: formDataBreakPoints, - numBuckets: formDataNumBuckets, - linearColorScheme, + break_points: formDataBreakPoints, + num_buckets: formDataNumBuckets, + linear_color_scheme: linearColorScheme, opacity, }, features, accessor) { const breakPoints = formDataBreakPoints || formDataNumBuckets ? getBreakPoints({ - breakPoints: formDataBreakPoints, - numBuckets: formDataNumBuckets, + break_points: formDataBreakPoints, + num_buckets: formDataNumBuckets, }, features, accessor) : null; const colorScheme = Array.isArray(linearColorScheme) From 538776b47025f8ff97f293f943736cb3633b2702 Mon Sep 17 00:00:00 2001 From: Beto Dealmeida Date: Wed, 3 Apr 2019 12:11:08 -0700 Subject: [PATCH 12/38] Fetch charts with GET to benefit from browser cache and conditional requests (#7032) * Sparkline dates aren't formatting in Time Series Table (#6976) * Exclude venv for python linter to ignore * Fix NaN error * Fix the white background shown in SQL editor on drag (#7021) This PR sets the background-color css property on `.ace_scroller` instead of `.ace_content` to prevent the white background shown during resizing of the SQL editor before drag ends. * Show tooltip with time frame (#6979) * Fix time filter control (#6978) * Enhancement of query context and object. (#6962) * added more functionalities for query context and object. * fixed cache logic * added default value for groupby * updated comments and removed print (cherry picked from commit d5b9795f87f79fa2c41e144ffc00fd9586be7657) * [fix] /superset/slice/id url is too long (#6989) (cherry picked from commit 6a4d507ab607b01ed324cb3341b71c6fb2cb5c97) * [WIP] fix user specified JSON metadata not updating dashboard on refresh (#7027) (cherry picked from commit cc58f0e661044e95c7c86d0da8d77a0a6640efe7) * feat: add ability to change font size in big number (#7003) * Add ability to change font sizes in Big Number * rename big number to header * Add comment to clarify font size values * Allow LIMIT to be specified in parameters (#7052) * [fix] Cursor jumping when editing chart and dashboard titles (#7038) (cherry picked from commit fc1770f7b79a4d8815b646b46390fabf190c3815) * Changing time table viz to pass formatTime a date (#7020) (cherry picked from commit 7f3c145b1f5a4e2d8b95982119503e98772e2c47) * [db-engine-spec] Aligning Hive/Presto partition logic (#7007) (cherry picked from commit 05be86611785fef2904992e4e7d31dce23f1c51b) * [fix] explore chart from dashboard missed slice title (#7046) (cherry picked from commit a6d48d4052839286aec725d51303b3b2bf6e8dd4) * fix inaccurate data calculation with adata rolling and contribution (#7035) (cherry picked from commit 0782e831cd37f665a2838119d87c433269f1b36b) * Adding warning message for sqllab save query (#7028) (cherry picked from commit ead3d48133e7e1ab8b91d51e561544a544b4eaad) * [datasource] Ensuring consistent behavior of datasource editing/saving. (#7037) * Update datasource.py * Update datasource.py (cherry picked from commit c771625f1068d3a7f41e6bced14b0cbdbf9962cc) * [csv-upload] Fixing message encoding (#6971) (cherry picked from commit 48431ab5b9375a94c5262a0336d9c69e5f01a3ac) * [sql-parse] Fixing LIMIT exceptions (#6963) (cherry picked from commit 3e076cb60b385e675ed1c9a8053493375e43370b) * Adding custom control overrides (#6956) * Adding extraOverrides to line chart * Updating extraOverrides to fit with more cases * Moving extraOverrides to index.js * Removing webpack-merge in package.json * Fixing metrics control clearing metric (cherry picked from commit e6194051f486e42922dc4e34a861f4490c1062fc) * [sqlparse] Fixing table name extraction for ill-defined query (#7029) (cherry picked from commit 07c340cf8203f13222f16efad1e55e202deb1865) * [missing values] Removing replacing missing values (#4905) (cherry picked from commit 61add606ca16a6ba981ccde864b121f5464b697a) * [SQL Lab] Improved query and results tabs rendering reliability (#7082) closes #7080 (cherry picked from commit 9b58e9f4920ef424e5b545dcbb4726e22bed5982) * Fix filter_box migration PR #6523 (#7066) * Fix filter_box migration PR #6523 * Fix druid-related bug (cherry picked from commit b210742ad24d01ca05bc58ca3342c90e301fe073) * SQL editor layout makeover (#7102) This PR includes the following layout and css tweaks: - Using flex to layout the north and south sub panes of query pane so resizing works properly in both Chrome and Firefox - Removal of necessary wrapper divs and tweaking of css in sql lab so we can scroll to the bottom of both the table list and the results pane - Make sql lab's content not overflow vertically and layout the query result area to eliminate double scroll bars - css tweaks on the basic.html page so the loading animation appears in the center of the page across the board (cherry picked from commit 71f1bbd2ec59b99d6ba6d9a4a2f9cfceaf922b80) * [forms] Fix handling of NULLs (cherry picked from commit e83a07d3dfda350cc44041cb6cbaec4510887902) * handle null column_name in sqla and druid models (cherry picked from commit 2ff721ae072b8d69c5cabddc3e1a388a596b1b6f) * Use metric name instead of metric in filter box (#7106) (cherry picked from commit 003364e74ea70cad1a4a6e784933fe8bef4c78ec) * Bump python lib croniter to an existing version (#7132) Package maintainers should really never delete packages, but it appears this happened with croniter and resulted in breaking our builds. This PR bumps to a more recent existing version of the library (cherry picked from commit 215ed392a11598eac228f57341dbfd232cf770e3) * Revert PR #6933 (#7162) * Add decorator for etag cache * Fetch charts with GET * Small fixes * Fix typo * Compute correct cache key; fix logging * Check perms on cached response * Revert change * If perms fail, return naked response * Fix lint * Compute cache key from all form data * Pass extra_filters in GET request * Fix pylint * Fix flake8 * Use ETags even if no cache is set * Handle adhoc filters * Raise in debug mode * Rename actions * Fix integration tests * Do POST request on new charts * Set extra/adhoc filters only in GET requests * Raise if check_perms fails * Refactor auth * Fix flake8 * Fix js unit tests * Fix js unit tests that fail in lyftga * Fix js * Sparkline dates aren't formatting in Time Series Table (#6976) * Exclude venv for python linter to ignore * Fix NaN error * Changing time table viz to pass formatTime a date (#7020) (cherry picked from commit 7f3c145b1f5a4e2d8b95982119503e98772e2c47) * SQL editor layout makeover (#7102) This PR includes the following layout and css tweaks: - Using flex to layout the north and south sub panes of query pane so resizing works properly in both Chrome and Firefox - Removal of necessary wrapper divs and tweaking of css in sql lab so we can scroll to the bottom of both the table list and the results pane - Make sql lab's content not overflow vertically and layout the query result area to eliminate double scroll bars - css tweaks on the basic.html page so the loading animation appears in the center of the page across the board (cherry picked from commit 71f1bbd2ec59b99d6ba6d9a4a2f9cfceaf922b80) * Add decorator for etag cache * Fetch charts with GET * Small fixes * Fix typo * Compute correct cache key; fix logging * Check perms on cached response * Revert change * If perms fail, return naked response * Fix lint * Compute cache key from all form data * Pass extra_filters in GET request * Fix pylint * Fix flake8 * Use ETags even if no cache is set * Handle adhoc filters * Raise in debug mode * Rename actions * Fix integration tests * Do POST request on new charts * Set extra/adhoc filters only in GET requests * Raise if check_perms fails * Refactor auth * Fix flake8 * Fix js unit tests * Fix js unit tests that fail in lyftga * Fix js * Fix bad merge * Use far future when max_age=0 --- .../cypress/integration/dashboard/controls.js | 2 +- .../integration/dashboard/edit_mode.js | 2 +- .../cypress/integration/dashboard/filter.js | 2 +- .../cypress/integration/dashboard/load.js | 2 +- .../cypress/integration/dashboard/save.js | 2 +- .../integration/explore/control.test.js | 36 +- .../cypress/integration/explore/link.test.js | 9 +- superset/assets/package-lock.json | 362 +++++++++--------- .../javascripts/chart/chartActions_spec.js | 14 +- .../dashboard/components/Dashboard_spec.jsx | 10 +- superset/assets/src/chart/Chart.jsx | 23 +- superset/assets/src/chart/chartAction.js | 36 +- .../src/dashboard/components/Dashboard.jsx | 4 +- .../src/dashboard/containers/Dashboard.jsx | 4 +- .../explore/components/ExploreChartHeader.jsx | 6 +- .../components/ExploreViewContainer.jsx | 5 +- superset/assets/src/explore/exploreUtils.js | 15 +- superset/utils/decorators.py | 72 +++- superset/views/core.py | 200 ++++------ superset/views/utils.py | 118 +++++- 20 files changed, 553 insertions(+), 371 deletions(-) diff --git a/superset/assets/cypress/integration/dashboard/controls.js b/superset/assets/cypress/integration/dashboard/controls.js index fb103bde4..3e218d2ca 100644 --- a/superset/assets/cypress/integration/dashboard/controls.js +++ b/superset/assets/cypress/integration/dashboard/controls.js @@ -39,7 +39,7 @@ export default () => describe('top-level controls', () => { .forEach((id) => { const sliceRequest = `getJson_${id}`; sliceRequests.push(`@${sliceRequest}`); - cy.route('POST', `/superset/explore_json/?form_data={"slice_id":${id}}`).as(sliceRequest); + cy.route('GET', `/superset/explore_json/?form_data={"slice_id":${id}}`).as(sliceRequest); const forceRefresh = `getJson_${id}_force`; forceRefreshRequests.push(`@${forceRefresh}`); diff --git a/superset/assets/cypress/integration/dashboard/edit_mode.js b/superset/assets/cypress/integration/dashboard/edit_mode.js index e58e7df53..d9395d276 100644 --- a/superset/assets/cypress/integration/dashboard/edit_mode.js +++ b/superset/assets/cypress/integration/dashboard/edit_mode.js @@ -29,7 +29,7 @@ export default () => describe('edit mode', () => { const dashboard = bootstrapData.dashboard_data; const boxplotChartId = dashboard.slices.find(slice => (slice.form_data.viz_type === 'box_plot')).slice_id; const boxplotRequest = `/superset/explore_json/?form_data={"slice_id":${boxplotChartId}}`; - cy.route('POST', boxplotRequest).as('boxplotRequest'); + cy.route('GET', boxplotRequest).as('boxplotRequest'); }); cy.get('.dashboard-header').contains('Edit dashboard').click(); diff --git a/superset/assets/cypress/integration/dashboard/filter.js b/superset/assets/cypress/integration/dashboard/filter.js index 157cbe868..6ec1c9261 100644 --- a/superset/assets/cypress/integration/dashboard/filter.js +++ b/superset/assets/cypress/integration/dashboard/filter.js @@ -40,7 +40,7 @@ export default () => describe('dashboard filter', () => { const aliases = []; const filterRoute = `/superset/explore_json/?form_data={"slice_id":${filterId}}`; - cy.route('POST', filterRoute).as('fetchFilter'); + cy.route('GET', filterRoute).as('fetchFilter'); cy.wait('@fetchFilter'); sliceIds .filter(id => (parseInt(id, 10) !== filterId)) diff --git a/superset/assets/cypress/integration/dashboard/load.js b/superset/assets/cypress/integration/dashboard/load.js index 30c9d325d..0dbe1ff14 100644 --- a/superset/assets/cypress/integration/dashboard/load.js +++ b/superset/assets/cypress/integration/dashboard/load.js @@ -34,7 +34,7 @@ export default () => describe('load', () => { // then define routes and create alias for each requests slices.forEach((slice) => { const alias = `getJson_${slice.slice_id}`; - cy.route('POST', `/superset/explore_json/?form_data={"slice_id":${slice.slice_id}}`).as(alias); + cy.route('GET', `/superset/explore_json/?form_data={"slice_id":${slice.slice_id}}`).as(alias); aliases.push(`@${alias}`); }); }); diff --git a/superset/assets/cypress/integration/dashboard/save.js b/superset/assets/cypress/integration/dashboard/save.js index 1c673b0c7..d144a71ca 100644 --- a/superset/assets/cypress/integration/dashboard/save.js +++ b/superset/assets/cypress/integration/dashboard/save.js @@ -57,7 +57,7 @@ export default () => describe('save', () => { // should have box_plot chart const boxplotRequest = `/superset/explore_json/?form_data={"slice_id":${boxplotChartId}}`; - cy.route('POST', boxplotRequest).as('boxplotRequest'); + cy.route('GET', boxplotRequest).as('boxplotRequest'); cy.wait('@boxplotRequest'); cy.get('.grid-container .box_plot').should('be.exist'); diff --git a/superset/assets/cypress/integration/explore/control.test.js b/superset/assets/cypress/integration/explore/control.test.js index ba4636c47..711ab7834 100644 --- a/superset/assets/cypress/integration/explore/control.test.js +++ b/superset/assets/cypress/integration/explore/control.test.js @@ -26,7 +26,8 @@ describe('Groupby', () => { cy.server(); cy.login(); - cy.route('POST', '/superset/explore_json/**').as('getJson'); + cy.route('GET', '/superset/explore_json/**').as('getJson'); + cy.route('POST', '/superset/explore_json/**').as('postJson'); cy.visitChartByName('Num Births Trend'); cy.verifySliceSuccess({ waitAlias: '@getJson' }); @@ -36,7 +37,7 @@ describe('Groupby', () => { cy.get('.VirtualizedSelectFocusedOption').click(); }); cy.get('button.query').click(); - cy.verifySliceSuccess({ waitAlias: '@getJson', chartSelector: 'svg' }); + cy.verifySliceSuccess({ waitAlias: '@postJson', chartSelector: 'svg' }); }); }); @@ -44,7 +45,8 @@ describe('AdhocMetrics', () => { beforeEach(() => { cy.login(); cy.server(); - cy.route('POST', '/superset/explore_json/**').as('getJson'); + cy.route('GET', '/superset/explore_json/**').as('getJson'); + cy.route('POST', '/superset/explore_json/**').as('postJson'); }); it('Clear metric and set simple adhoc metric', () => { @@ -74,7 +76,7 @@ describe('AdhocMetrics', () => { cy.get('button.query').click(); cy.verifySliceSuccess({ - waitAlias: '@getJson', + waitAlias: '@postJson', querySubstring: metricName, chartSelector: 'svg', }); @@ -105,7 +107,7 @@ describe('AdhocMetrics', () => { cy.get('button.query').click(); cy.verifySliceSuccess({ - waitAlias: '@getJson', + waitAlias: '@postJson', querySubstring: metric, chartSelector: 'svg', }); @@ -137,7 +139,7 @@ describe('AdhocMetrics', () => { cy.get('button.query').click(); cy.verifySliceSuccess({ - waitAlias: '@getJson', + waitAlias: '@postJson', chartSelector: 'svg', }); }); @@ -147,7 +149,8 @@ describe('AdhocFilters', () => { beforeEach(() => { cy.login(); cy.server(); - cy.route('POST', '/superset/explore_json/**').as('getJson'); + cy.route('GET', '/superset/explore_json/**').as('getJson'); + cy.route('POST', '/superset/explore_json/**').as('postJson'); }); it('Set simple adhoc filter', () => { @@ -177,7 +180,7 @@ describe('AdhocFilters', () => { cy.get('button.query').click(); cy.verifySliceSuccess({ - waitAlias: '@getJson', + waitAlias: '@postJson', chartSelector: 'svg', }); }); @@ -206,7 +209,7 @@ describe('AdhocFilters', () => { cy.get('button.query').click(); cy.verifySliceSuccess({ - waitAlias: '@getJson', + waitAlias: '@postJson', chartSelector: 'svg', }); }); @@ -217,7 +220,8 @@ describe('Advanced analytics', () => { beforeEach(() => { cy.login(); cy.server(); - cy.route('POST', '/superset/explore_json/**').as('getJson'); + cy.route('GET', '/superset/explore_json/**').as('getJson'); + cy.route('POST', '/superset/explore_json/**').as('postJson'); }); it('Create custom time compare', () => { @@ -240,7 +244,7 @@ describe('Advanced analytics', () => { }); cy.get('button.query').click(); - cy.wait('@getJson'); + cy.wait('@postJson'); cy.reload(); cy.verifySliceSuccess({ waitAlias: '@getJson', @@ -257,7 +261,8 @@ describe('Annotations', () => { beforeEach(() => { cy.login(); cy.server(); - cy.route('POST', '/superset/explore_json/**').as('getJson'); + cy.route('GET', '/superset/explore_json/**').as('getJson'); + cy.route('POST', '/superset/explore_json/**').as('postJson'); }); it('Create formula annotation y-axis goal line', () => { @@ -280,7 +285,7 @@ describe('Annotations', () => { cy.get('button.query').click(); cy.verifySliceSuccess({ - waitAlias: '@getJson', + waitAlias: '@postJson', chartSelector: 'svg', }); @@ -292,7 +297,8 @@ describe('Time range filter', () => { beforeEach(() => { cy.login(); cy.server(); - cy.route('POST', '/superset/explore_json/**').as('getJson'); + cy.route('GET', '/superset/explore_json/**').as('getJson'); + cy.route('POST', '/superset/explore_json/**').as('postJson'); }); it('Defaults to the correct tab for time_range params', () => { @@ -304,7 +310,7 @@ describe('Time range filter', () => { }; cy.visitChartByParams(JSON.stringify(formData)); - cy.verifySliceSuccess({ waitAlias: '@getJson' }); + cy.verifySliceSuccess({ waitAlias: '@postJson' }); cy.get('[data-test=time_range]').within(() => { cy.get('span.label').click(); diff --git a/superset/assets/cypress/integration/explore/link.test.js b/superset/assets/cypress/integration/explore/link.test.js index 024612f53..36b56ce5d 100644 --- a/superset/assets/cypress/integration/explore/link.test.js +++ b/superset/assets/cypress/integration/explore/link.test.js @@ -26,7 +26,8 @@ describe('Test explore links', () => { beforeEach(() => { cy.login(); cy.server(); - cy.route('POST', '/superset/explore_json/**').as('getJson'); + cy.route('GET', '/superset/explore_json/**').as('getJson'); + cy.route('POST', '/superset/explore_json/**').as('postJson'); }); it('Open and close view query modal', () => { @@ -35,7 +36,7 @@ describe('Test explore links', () => { cy.get('button#query').click(); cy.get('span').contains('View query').parent().click(); - cy.wait('@getJson').then(() => { + cy.wait('@postJson').then(() => { cy.get('code'); }); cy.get('.modal-header').within(() => { @@ -83,7 +84,7 @@ describe('Test explore links', () => { const newChartName = 'Test chart'; cy.visitChartByParams(JSON.stringify(formData)); - cy.verifySliceSuccess({ waitAlias: '@getJson' }); + cy.verifySliceSuccess({ waitAlias: '@postJson' }); cy.url().then((url) => { cy.get('button[data-target="#save_modal"]').click(); cy.get('.modal-content').within(() => { @@ -109,7 +110,7 @@ describe('Test explore links', () => { cy.get('.modal-content').within(() => { cy.get('button#btn_modal_save').click(); }); - cy.verifySliceSuccess({ waitAlias: '@getJson' }); + cy.verifySliceSuccess({ waitAlias: '@postJson' }); cy.request(`/chart/api/read?_flt_3_slice_name=${chartName}`).then((response) => { cy.request('DELETE', `/chart/api/delete/${response.body.pks[0]}`); }); diff --git a/superset/assets/package-lock.json b/superset/assets/package-lock.json index 35b3c3ad0..8eeca9bde 100644 --- a/superset/assets/package-lock.json +++ b/superset/assets/package-lock.json @@ -1368,7 +1368,7 @@ }, "@data-ui/event-flow": { "version": "0.0.54", - "resolved": "https://registry.npmjs.org/@data-ui/event-flow/-/event-flow-0.0.54.tgz", + "resolved": "http://registry.npmjs.org/@data-ui/event-flow/-/event-flow-0.0.54.tgz", "integrity": "sha1-uwPh/StWNCSGVbjfnTxsOKdH5l4=", "requires": { "@data-ui/forms": "0.0.50", @@ -1659,7 +1659,7 @@ }, "@data-ui/radial-chart": { "version": "0.0.54", - "resolved": "https://registry.npmjs.org/@data-ui/radial-chart/-/radial-chart-0.0.54.tgz", + "resolved": "http://registry.npmjs.org/@data-ui/radial-chart/-/radial-chart-0.0.54.tgz", "integrity": "sha1-DSiwdoHZtgJ9msI7cpJBgn1RMAE=", "requires": { "@data-ui/shared": "0.0.54", @@ -1674,7 +1674,7 @@ }, "@data-ui/shared": { "version": "0.0.54", - "resolved": "https://registry.npmjs.org/@data-ui/shared/-/shared-0.0.54.tgz", + "resolved": "http://registry.npmjs.org/@data-ui/shared/-/shared-0.0.54.tgz", "integrity": "sha1-L7DW3ukNrCC/jzwpE8aFCoIj1Zs=", "requires": { "@data-ui/theme": "0.0.48", @@ -1747,7 +1747,7 @@ }, "@data-ui/sparkline": { "version": "0.0.54", - "resolved": "https://registry.npmjs.org/@data-ui/sparkline/-/sparkline-0.0.54.tgz", + "resolved": "http://registry.npmjs.org/@data-ui/sparkline/-/sparkline-0.0.54.tgz", "integrity": "sha1-zj0WbZ4LI5oLoC84lMuejIQXHO8=", "requires": { "@data-ui/shared": "0.0.54", @@ -2201,7 +2201,7 @@ }, "@sinonjs/formatio": { "version": "2.0.0", - "resolved": "https://registry.npmjs.org/@sinonjs/formatio/-/formatio-2.0.0.tgz", + "resolved": "http://registry.npmjs.org/@sinonjs/formatio/-/formatio-2.0.0.tgz", "integrity": "sha512-ls6CAMA6/5gG+O/IdsBcblvnd8qcO/l1TYoNeAzp3wcISOxlPXQEus0mLcdwazEkWjaBdaJ3TaxmNgCLWwvWzg==", "dev": true, "requires": { @@ -3307,7 +3307,7 @@ }, "acorn-jsx": { "version": "3.0.1", - "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-3.0.1.tgz", + "resolved": "http://registry.npmjs.org/acorn-jsx/-/acorn-jsx-3.0.1.tgz", "integrity": "sha1-r9+UiPsezvyDSPb7IvRk4ypYs2s=", "dev": true, "requires": { @@ -3316,7 +3316,7 @@ "dependencies": { "acorn": { "version": "3.3.0", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-3.3.0.tgz", + "resolved": "http://registry.npmjs.org/acorn/-/acorn-3.3.0.tgz", "integrity": "sha1-ReN/s56No/JbruP/U2niu18iAXo=", "dev": true } @@ -3751,7 +3751,7 @@ }, "array-equal": { "version": "1.0.0", - "resolved": "https://registry.npmjs.org/array-equal/-/array-equal-1.0.0.tgz", + "resolved": "http://registry.npmjs.org/array-equal/-/array-equal-1.0.0.tgz", "integrity": "sha1-jCpe8kcv2ep0KwTHenUJO6J1fJM=", "dev": true }, @@ -3874,7 +3874,7 @@ }, "util": { "version": "0.10.3", - "resolved": "https://registry.npmjs.org/util/-/util-0.10.3.tgz", + "resolved": "http://registry.npmjs.org/util/-/util-0.10.3.tgz", "integrity": "sha1-evsa/lCAUkZInj23/g7TeTNqwPk=", "dev": true, "requires": { @@ -4364,7 +4364,7 @@ }, "babel-plugin-syntax-dynamic-import": { "version": "6.18.0", - "resolved": "https://registry.npmjs.org/babel-plugin-syntax-dynamic-import/-/babel-plugin-syntax-dynamic-import-6.18.0.tgz", + "resolved": "http://registry.npmjs.org/babel-plugin-syntax-dynamic-import/-/babel-plugin-syntax-dynamic-import-6.18.0.tgz", "integrity": "sha1-jWomIpyDdFqZgqRBBRVyyqF5sdo=", "dev": true }, @@ -4648,7 +4648,7 @@ }, "brace": { "version": "0.11.1", - "resolved": "https://registry.npmjs.org/brace/-/brace-0.11.1.tgz", + "resolved": "http://registry.npmjs.org/brace/-/brace-0.11.1.tgz", "integrity": "sha1-SJb8ydVE7vRfS7dmDbMg07N5/lg=" }, "brace-expansion": { @@ -4680,7 +4680,7 @@ }, "brfs": { "version": "1.6.1", - "resolved": "https://registry.npmjs.org/brfs/-/brfs-1.6.1.tgz", + "resolved": "http://registry.npmjs.org/brfs/-/brfs-1.6.1.tgz", "integrity": "sha512-OfZpABRQQf+Xsmju8XE9bDjs+uU4vLREGolP7bDgcpsI17QREyZ4Bl+2KLxxx1kCgA0fAIhKQBaBYh+PEcCqYQ==", "requires": { "quote-stream": "^1.0.1", @@ -4712,7 +4712,7 @@ "dependencies": { "resolve": { "version": "1.1.7", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.1.7.tgz", + "resolved": "http://registry.npmjs.org/resolve/-/resolve-1.1.7.tgz", "integrity": "sha1-IDEU2CrSxe2ejgQRs5ModeiJ6Xs=", "dev": true } @@ -4720,7 +4720,7 @@ }, "browserify-aes": { "version": "1.2.0", - "resolved": "https://registry.npmjs.org/browserify-aes/-/browserify-aes-1.2.0.tgz", + "resolved": "http://registry.npmjs.org/browserify-aes/-/browserify-aes-1.2.0.tgz", "integrity": "sha512-+7CHXqGuspUn/Sl5aO7Ea0xWGAtETPXNSAjHo48JfLdPWcMng33Xe4znFvQweqc/uzk5zSOI3H52CYnjCfb5hA==", "dev": true, "requires": { @@ -4757,7 +4757,7 @@ }, "browserify-rsa": { "version": "4.0.1", - "resolved": "https://registry.npmjs.org/browserify-rsa/-/browserify-rsa-4.0.1.tgz", + "resolved": "http://registry.npmjs.org/browserify-rsa/-/browserify-rsa-4.0.1.tgz", "integrity": "sha1-IeCr+vbyApzy+vsTNWenAdQTVSQ=", "dev": true, "requires": { @@ -4809,7 +4809,7 @@ }, "buffer": { "version": "4.9.1", - "resolved": "https://registry.npmjs.org/buffer/-/buffer-4.9.1.tgz", + "resolved": "http://registry.npmjs.org/buffer/-/buffer-4.9.1.tgz", "integrity": "sha1-bRu2AbB6TvztlwlBMgkwJ8lbwpg=", "dev": true, "requires": { @@ -4942,7 +4942,7 @@ "dependencies": { "callsites": { "version": "2.0.0", - "resolved": "https://registry.npmjs.org/callsites/-/callsites-2.0.0.tgz", + "resolved": "http://registry.npmjs.org/callsites/-/callsites-2.0.0.tgz", "integrity": "sha1-BuuE8A7qQT2oav/vrL/7Ngk7PFA=", "dev": true } @@ -5261,7 +5261,7 @@ }, "clean-webpack-plugin": { "version": "0.1.19", - "resolved": "https://registry.npmjs.org/clean-webpack-plugin/-/clean-webpack-plugin-0.1.19.tgz", + "resolved": "http://registry.npmjs.org/clean-webpack-plugin/-/clean-webpack-plugin-0.1.19.tgz", "integrity": "sha512-M1Li5yLHECcN2MahoreuODul5LkjohJGFxLPTjl3j1ttKrF5rgjZET1SJduuqxLAuT1gAPOdkhg03qcaaU1KeA==", "dev": true, "requires": { @@ -5672,7 +5672,7 @@ }, "create-hash": { "version": "1.2.0", - "resolved": "https://registry.npmjs.org/create-hash/-/create-hash-1.2.0.tgz", + "resolved": "http://registry.npmjs.org/create-hash/-/create-hash-1.2.0.tgz", "integrity": "sha512-z00bCGNHDG8mHAkP7CtT1qVu+bFQUPjYq/4Iv3C3kWjTFV10zIjfSoeqXo9Asws8gwSHDGj/hl2u4OGIjapeCg==", "dev": true, "requires": { @@ -5685,7 +5685,7 @@ }, "create-hmac": { "version": "1.1.7", - "resolved": "https://registry.npmjs.org/create-hmac/-/create-hmac-1.1.7.tgz", + "resolved": "http://registry.npmjs.org/create-hmac/-/create-hmac-1.1.7.tgz", "integrity": "sha512-MJG9liiZ+ogc4TzUwuvbER1JRdgvUFSB5+VR/g5h82fGaIRWMWddtKBHi7/sVhfjQZ6SehlyhvQYrcYkaUIpLg==", "dev": true, "requires": { @@ -5746,7 +5746,7 @@ }, "css-color-names": { "version": "0.0.4", - "resolved": "https://registry.npmjs.org/css-color-names/-/css-color-names-0.0.4.tgz", + "resolved": "http://registry.npmjs.org/css-color-names/-/css-color-names-0.0.4.tgz", "integrity": "sha1-gIrcLnnPhHOAabZGyyDsJ762KeA=", "dev": true }, @@ -5810,7 +5810,7 @@ }, "css-in-js-utils": { "version": "2.0.1", - "resolved": "https://registry.npmjs.org/css-in-js-utils/-/css-in-js-utils-2.0.1.tgz", + "resolved": "http://registry.npmjs.org/css-in-js-utils/-/css-in-js-utils-2.0.1.tgz", "integrity": "sha512-PJF0SpJT+WdbVVt0AOYp9C8GnuruRlL/UFW7932nLWmFLQTaWEzTBQEx7/hn4BuV+WON75iAViSUJLiU3PKbpA==", "requires": { "hyphenate-style-name": "^1.0.2", @@ -5870,7 +5870,7 @@ }, "css-select": { "version": "1.2.0", - "resolved": "https://registry.npmjs.org/css-select/-/css-select-1.2.0.tgz", + "resolved": "http://registry.npmjs.org/css-select/-/css-select-1.2.0.tgz", "integrity": "sha1-KzoRBTnFNV8c2NMUYj6HCxIeyFg=", "dev": true, "requires": { @@ -6258,7 +6258,7 @@ }, "d3-geo-projection": { "version": "0.2.16", - "resolved": "https://registry.npmjs.org/d3-geo-projection/-/d3-geo-projection-0.2.16.tgz", + "resolved": "http://registry.npmjs.org/d3-geo-projection/-/d3-geo-projection-0.2.16.tgz", "integrity": "sha1-SZTs0QM92xUztsTFUoocgdzClCc=", "requires": { "brfs": "^1.3.0" @@ -6676,7 +6676,7 @@ }, "diffie-hellman": { "version": "5.0.3", - "resolved": "https://registry.npmjs.org/diffie-hellman/-/diffie-hellman-5.0.3.tgz", + "resolved": "http://registry.npmjs.org/diffie-hellman/-/diffie-hellman-5.0.3.tgz", "integrity": "sha512-kqag/Nl+f3GwyK25fhUMYj81BUOrZ9IuJsjIcDE5icNM9FJHAVm3VcUDxdLPoQtTuUylWm6ZIknYJwwaPxsUzg==", "dev": true, "requires": { @@ -6706,7 +6706,7 @@ }, "dnd-core": { "version": "2.6.0", - "resolved": "https://registry.npmjs.org/dnd-core/-/dnd-core-2.6.0.tgz", + "resolved": "http://registry.npmjs.org/dnd-core/-/dnd-core-2.6.0.tgz", "integrity": "sha1-ErrWbVh0LG5ffPKUP7aFlED4CcQ=", "requires": { "asap": "^2.0.6", @@ -6836,7 +6836,7 @@ }, "duplexer": { "version": "0.1.1", - "resolved": "https://registry.npmjs.org/duplexer/-/duplexer-0.1.1.tgz", + "resolved": "http://registry.npmjs.org/duplexer/-/duplexer-0.1.1.tgz", "integrity": "sha1-rOb/gIwc5mtX0ev5eXessCM0z8E=", "dev": true }, @@ -7078,7 +7078,7 @@ }, "es6-promise": { "version": "3.3.1", - "resolved": "https://registry.npmjs.org/es6-promise/-/es6-promise-3.3.1.tgz", + "resolved": "http://registry.npmjs.org/es6-promise/-/es6-promise-3.3.1.tgz", "integrity": "sha1-oIzd6EzNvzTQJ6FFG8kdS80ophM=" }, "es6bindall": { @@ -7125,7 +7125,7 @@ }, "eslint": { "version": "4.19.1", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-4.19.1.tgz", + "resolved": "http://registry.npmjs.org/eslint/-/eslint-4.19.1.tgz", "integrity": "sha512-bT3/1x1EbZB7phzYu7vCr1v3ONuzDtX8WjuM9c0iYxe+cq+pwcKEoQjl7zd3RpC6YOLgnSy3cTN58M2jcoPDIQ==", "dev": true, "requires": { @@ -7229,7 +7229,7 @@ }, "fast-deep-equal": { "version": "1.1.0", - "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-1.1.0.tgz", + "resolved": "http://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-1.1.0.tgz", "integrity": "sha1-wFNHeBfIa1HaqFPIHgWbcz0CNhQ=", "dev": true }, @@ -7403,7 +7403,7 @@ "dependencies": { "doctrine": { "version": "1.5.0", - "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-1.5.0.tgz", + "resolved": "http://registry.npmjs.org/doctrine/-/doctrine-1.5.0.tgz", "integrity": "sha1-N53Ocw9hZvds76TmcHoVmwLFpvo=", "dev": true, "requires": { @@ -7413,7 +7413,7 @@ }, "load-json-file": { "version": "2.0.0", - "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-2.0.0.tgz", + "resolved": "http://registry.npmjs.org/load-json-file/-/load-json-file-2.0.0.tgz", "integrity": "sha1-eUfkIUmvgNaWy/eXvKq8/h/inKg=", "dev": true, "requires": { @@ -7551,7 +7551,7 @@ }, "espree": { "version": "3.5.4", - "resolved": "https://registry.npmjs.org/espree/-/espree-3.5.4.tgz", + "resolved": "http://registry.npmjs.org/espree/-/espree-3.5.4.tgz", "integrity": "sha512-yAcIQxtmMiB/jL32dzEp2enBeidsB7xWPLNiw3IIkpVds1P+h7qF9YwJq1yUNzp2OKXgAprs4F61ih66UsoD1A==", "dev": true, "requires": { @@ -7731,7 +7731,7 @@ "dependencies": { "source-map": { "version": "0.5.0", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.0.tgz", + "resolved": "http://registry.npmjs.org/source-map/-/source-map-0.5.0.tgz", "integrity": "sha1-D+llA6yGpa213mP05BKuSHLNvoY=", "dev": true } @@ -7777,7 +7777,7 @@ "dependencies": { "array-flatten": { "version": "1.1.1", - "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", + "resolved": "http://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", "integrity": "sha1-ml9pkFGx5wczKPKgCJaLZOopVdI=", "dev": true }, @@ -7805,7 +7805,7 @@ }, "external-editor": { "version": "2.2.0", - "resolved": "https://registry.npmjs.org/external-editor/-/external-editor-2.2.0.tgz", + "resolved": "http://registry.npmjs.org/external-editor/-/external-editor-2.2.0.tgz", "integrity": "sha512-bSn6gvGxKt+b7+6TKEv1ZycHleA7aHhRHyAqJyp5pbUFuYYNIzpZnQDk7AsYckyWdEnTeAnay0aCy2aV6iTk9A==", "dev": true, "requires": { @@ -8027,7 +8027,7 @@ }, "file-loader": { "version": "1.1.11", - "resolved": "https://registry.npmjs.org/file-loader/-/file-loader-1.1.11.tgz", + "resolved": "http://registry.npmjs.org/file-loader/-/file-loader-1.1.11.tgz", "integrity": "sha512-TGR4HU7HUsGg6GCOPJnFk06RhWgEWFLAGWiT6rcD+GRC2keU3s9RGJ+b3Z6/U73jwwNb2gKLJ7YCrp+jvU4ALg==", "dev": true, "requires": { @@ -8037,7 +8037,7 @@ }, "file-type": { "version": "3.9.0", - "resolved": "https://registry.npmjs.org/file-type/-/file-type-3.9.0.tgz", + "resolved": "http://registry.npmjs.org/file-type/-/file-type-3.9.0.tgz", "integrity": "sha1-JXoHg4TR24CHvESdEH1SpSZyuek=" }, "fileset": { @@ -8070,7 +8070,7 @@ }, "finalhandler": { "version": "1.1.1", - "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.1.1.tgz", + "resolved": "http://registry.npmjs.org/finalhandler/-/finalhandler-1.1.1.tgz", "integrity": "sha512-Y1GUDo39ez4aHAw7MysnUD5JzYX+WaIj8I57kO3aEPT1fFRL4sr7mjei97FgnwhAyyzRYmQZaTHb2+9uZ1dPtg==", "dev": true, "requires": { @@ -8676,27 +8676,27 @@ "dependencies": { "abbrev": { "version": "1.1.1", - "resolved": false, + "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz", "integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==", "dev": true, "optional": true }, "ansi-regex": { "version": "2.1.1", - "resolved": false, - "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=", - "dev": true + "bundled": true, + "dev": true, + "optional": true }, "aproba": { "version": "1.2.0", - "resolved": false, + "resolved": "https://registry.npmjs.org/aproba/-/aproba-1.2.0.tgz", "integrity": "sha512-Y9J6ZjXtoYh8RnXVCMOU/ttDmk1aBjunq9vO0ta5x85WDQiQfUF9sIPBITdbiiIVcBo03Hi3jMxigBtsddlXRw==", "dev": true, "optional": true }, "are-we-there-yet": { "version": "1.1.4", - "resolved": false, + "resolved": "https://registry.npmjs.org/are-we-there-yet/-/are-we-there-yet-1.1.4.tgz", "integrity": "sha1-u13KOCu5TwXhUZQ3PRb9O6HKEQ0=", "dev": true, "optional": true, @@ -8707,15 +8707,15 @@ }, "balanced-match": { "version": "1.0.0", - "resolved": false, - "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=", - "dev": true + "bundled": true, + "dev": true, + "optional": true }, "brace-expansion": { "version": "1.1.11", - "resolved": false, - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "bundled": true, "dev": true, + "optional": true, "requires": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" @@ -8723,39 +8723,39 @@ }, "chownr": { "version": "1.0.1", - "resolved": false, + "resolved": "https://registry.npmjs.org/chownr/-/chownr-1.0.1.tgz", "integrity": "sha1-4qdQQqlVGQi+vSW4Uj1fl2nXkYE=", "dev": true, "optional": true }, "code-point-at": { "version": "1.1.0", - "resolved": false, - "integrity": "sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c=", - "dev": true + "bundled": true, + "dev": true, + "optional": true }, "concat-map": { "version": "0.0.1", - "resolved": false, - "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=", - "dev": true + "bundled": true, + "dev": true, + "optional": true }, "console-control-strings": { "version": "1.1.0", - "resolved": false, - "integrity": "sha1-PXz0Rk22RG6mRL9LOVB/mFEAjo4=", - "dev": true + "bundled": true, + "dev": true, + "optional": true }, "core-util-is": { "version": "1.0.2", - "resolved": false, + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=", "dev": true, "optional": true }, "debug": { "version": "2.6.9", - "resolved": false, + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", "dev": true, "optional": true, @@ -8765,28 +8765,28 @@ }, "deep-extend": { "version": "0.5.1", - "resolved": false, + "resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.5.1.tgz", "integrity": "sha512-N8vBdOa+DF7zkRrDCsaOXoCs/E2fJfx9B9MrKnnSiHNh4ws7eSys6YQE4KvT1cecKmOASYQBhbKjeuDD9lT81w==", "dev": true, "optional": true }, "delegates": { "version": "1.0.0", - "resolved": false, + "resolved": "https://registry.npmjs.org/delegates/-/delegates-1.0.0.tgz", "integrity": "sha1-hMbhWbgZBP3KWaDvRM2HDTElD5o=", "dev": true, "optional": true }, "detect-libc": { "version": "1.0.3", - "resolved": false, + "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-1.0.3.tgz", "integrity": "sha1-+hN8S9aY7fVc1c0CrFWfkaTEups=", "dev": true, "optional": true }, "fs-minipass": { "version": "1.2.5", - "resolved": false, + "resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-1.2.5.tgz", "integrity": "sha512-JhBl0skXjUPCFH7x6x61gQxrKyXsxB5gcgePLZCwfyCGGsTISMoIeObbrvVeP6Xmyaudw4TT43qV2Gz+iyd2oQ==", "dev": true, "optional": true, @@ -8796,14 +8796,14 @@ }, "fs.realpath": { "version": "1.0.0", - "resolved": false, + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=", "dev": true, "optional": true }, "gauge": { "version": "2.7.4", - "resolved": false, + "resolved": "https://registry.npmjs.org/gauge/-/gauge-2.7.4.tgz", "integrity": "sha1-LANAXHU4w51+s3sxcCLjJfsBi/c=", "dev": true, "optional": true, @@ -8820,7 +8820,7 @@ }, "glob": { "version": "7.1.2", - "resolved": false, + "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.2.tgz", "integrity": "sha512-MJTUg1kjuLeQCJ+ccE4Vpa6kKVXkPYJ2mOCQyUuKLcLQsdrMCpBPUi8qVE6+YuaJkozeA9NusTAw3hLr8Xe5EQ==", "dev": true, "optional": true, @@ -8835,14 +8835,14 @@ }, "has-unicode": { "version": "2.0.1", - "resolved": false, + "resolved": "https://registry.npmjs.org/has-unicode/-/has-unicode-2.0.1.tgz", "integrity": "sha1-4Ob+aijPUROIVeCG0Wkedx3iqLk=", "dev": true, "optional": true }, "iconv-lite": { "version": "0.4.21", - "resolved": false, + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.21.tgz", "integrity": "sha512-En5V9za5mBt2oUA03WGD3TwDv0MKAruqsuxstbMUZaj9W9k/m1CV/9py3l0L5kw9Bln8fdHQmzHSYtvpvTLpKw==", "dev": true, "optional": true, @@ -8852,7 +8852,7 @@ }, "ignore-walk": { "version": "3.0.1", - "resolved": false, + "resolved": "https://registry.npmjs.org/ignore-walk/-/ignore-walk-3.0.1.tgz", "integrity": "sha512-DTVlMx3IYPe0/JJcYP7Gxg7ttZZu3IInhuEhbchuqneY9wWe5Ojy2mXLBaQFUQmo0AW2r3qG7m1mg86js+gnlQ==", "dev": true, "optional": true, @@ -8862,7 +8862,7 @@ }, "inflight": { "version": "1.0.6", - "resolved": false, + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", "dev": true, "optional": true, @@ -8873,53 +8873,53 @@ }, "inherits": { "version": "2.0.3", - "resolved": false, - "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=", - "dev": true + "bundled": true, + "dev": true, + "optional": true }, "ini": { "version": "1.3.5", - "resolved": false, + "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.5.tgz", "integrity": "sha512-RZY5huIKCMRWDUqZlEi72f/lmXKMvuszcMBduliQ3nnWbx9X/ZBQO7DijMEYS9EhHBb2qacRUMtC7svLwe0lcw==", "dev": true, "optional": true }, "is-fullwidth-code-point": { "version": "1.0.0", - "resolved": false, - "integrity": "sha1-754xOG8DGn8NZDr4L95QxFfvAMs=", + "bundled": true, "dev": true, + "optional": true, "requires": { "number-is-nan": "^1.0.0" } }, "isarray": { "version": "1.0.0", - "resolved": false, + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=", "dev": true, "optional": true }, "minimatch": { "version": "3.0.4", - "resolved": false, - "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", + "bundled": true, "dev": true, + "optional": true, "requires": { "brace-expansion": "^1.1.7" } }, "minimist": { "version": "0.0.8", - "resolved": false, - "integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=", - "dev": true + "bundled": true, + "dev": true, + "optional": true }, "minipass": { "version": "2.2.4", - "resolved": false, - "integrity": "sha512-hzXIWWet/BzWhYs2b+u7dRHlruXhwdgvlTMDKC6Cb1U7ps6Ac6yQlR39xsbjWJE377YTCtKwIXIpJ5oP+j5y8g==", + "bundled": true, "dev": true, + "optional": true, "requires": { "safe-buffer": "^5.1.1", "yallist": "^3.0.0" @@ -8927,7 +8927,7 @@ }, "minizlib": { "version": "1.1.0", - "resolved": false, + "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-1.1.0.tgz", "integrity": "sha512-4T6Ur/GctZ27nHfpt9THOdRZNgyJ9FZchYO1ceg5S8Q3DNLCKYy44nCZzgCJgcvx2UM8czmqak5BCxJMrq37lA==", "dev": true, "optional": true, @@ -8937,23 +8937,23 @@ }, "mkdirp": { "version": "0.5.1", - "resolved": false, - "integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=", + "bundled": true, "dev": true, + "optional": true, "requires": { "minimist": "0.0.8" } }, "ms": { "version": "2.0.0", - "resolved": false, + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", "dev": true, "optional": true }, "needle": { "version": "2.2.0", - "resolved": false, + "resolved": "https://registry.npmjs.org/needle/-/needle-2.2.0.tgz", "integrity": "sha512-eFagy6c+TYayorXw/qtAdSvaUpEbBsDwDyxYFgLZ0lTojfH7K+OdBqAF7TAFwDokJaGpubpSGG0wO3iC0XPi8w==", "dev": true, "optional": true, @@ -8965,7 +8965,7 @@ }, "node-pre-gyp": { "version": "0.10.0", - "resolved": false, + "resolved": "https://registry.npmjs.org/node-pre-gyp/-/node-pre-gyp-0.10.0.tgz", "integrity": "sha512-G7kEonQLRbcA/mOoFoxvlMrw6Q6dPf92+t/l0DFSMuSlDoWaI9JWIyPwK0jyE1bph//CUEL65/Fz1m2vJbmjQQ==", "dev": true, "optional": true, @@ -8984,7 +8984,7 @@ }, "nopt": { "version": "4.0.1", - "resolved": false, + "resolved": "https://registry.npmjs.org/nopt/-/nopt-4.0.1.tgz", "integrity": "sha1-0NRoWv1UFRk8jHUFYC0NF81kR00=", "dev": true, "optional": true, @@ -8995,14 +8995,14 @@ }, "npm-bundled": { "version": "1.0.3", - "resolved": false, + "resolved": "https://registry.npmjs.org/npm-bundled/-/npm-bundled-1.0.3.tgz", "integrity": "sha512-ByQ3oJ/5ETLyglU2+8dBObvhfWXX8dtPZDMePCahptliFX2iIuhyEszyFk401PZUNQH20vvdW5MLjJxkwU80Ow==", "dev": true, "optional": true }, "npm-packlist": { "version": "1.1.10", - "resolved": false, + "resolved": "https://registry.npmjs.org/npm-packlist/-/npm-packlist-1.1.10.tgz", "integrity": "sha512-AQC0Dyhzn4EiYEfIUjCdMl0JJ61I2ER9ukf/sLxJUcZHfo+VyEfz2rMJgLZSS1v30OxPQe1cN0LZA1xbcaVfWA==", "dev": true, "optional": true, @@ -9013,7 +9013,7 @@ }, "npmlog": { "version": "4.1.2", - "resolved": false, + "resolved": "https://registry.npmjs.org/npmlog/-/npmlog-4.1.2.tgz", "integrity": "sha512-2uUqazuKlTaSI/dC8AzicUck7+IrEaOnN/e0jd3Xtt1KcGpwx30v50mL7oPyr/h9bL3E4aZccVwpwP+5W9Vjkg==", "dev": true, "optional": true, @@ -9026,43 +9026,43 @@ }, "number-is-nan": { "version": "1.0.1", - "resolved": false, - "integrity": "sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0=", - "dev": true + "bundled": true, + "dev": true, + "optional": true }, "object-assign": { "version": "4.1.1", - "resolved": false, + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=", "dev": true, "optional": true }, "once": { "version": "1.4.0", - "resolved": false, - "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", + "bundled": true, "dev": true, + "optional": true, "requires": { "wrappy": "1" } }, "os-homedir": { "version": "1.0.2", - "resolved": false, + "resolved": "https://registry.npmjs.org/os-homedir/-/os-homedir-1.0.2.tgz", "integrity": "sha1-/7xJiDNuDoM94MFox+8VISGqf7M=", "dev": true, "optional": true }, "os-tmpdir": { "version": "1.0.2", - "resolved": false, + "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz", "integrity": "sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ=", "dev": true, "optional": true }, "osenv": { "version": "0.1.5", - "resolved": false, + "resolved": "https://registry.npmjs.org/osenv/-/osenv-0.1.5.tgz", "integrity": "sha512-0CWcCECdMVc2Rw3U5w9ZjqX6ga6ubk1xDVKxtBQPK7wis/0F2r9T6k4ydGYhecl7YUBxBVxhL5oisPsNxAPe2g==", "dev": true, "optional": true, @@ -9073,21 +9073,21 @@ }, "path-is-absolute": { "version": "1.0.1", - "resolved": false, + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=", "dev": true, "optional": true }, "process-nextick-args": { "version": "2.0.0", - "resolved": false, + "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.0.tgz", "integrity": "sha512-MtEC1TqN0EU5nephaJ4rAtThHtC86dNN9qCuEhtshvpVBkAW5ZO7BASN9REnF9eoXGcRub+pFuKEpOHE+HbEMw==", "dev": true, "optional": true }, "rc": { "version": "1.2.7", - "resolved": false, + "resolved": "https://registry.npmjs.org/rc/-/rc-1.2.7.tgz", "integrity": "sha512-LdLD8xD4zzLsAT5xyushXDNscEjB7+2ulnl8+r1pnESlYtlJtVSoCMBGr30eDRJ3+2Gq89jK9P9e4tCEH1+ywA==", "dev": true, "optional": true, @@ -9100,7 +9100,7 @@ "dependencies": { "minimist": { "version": "1.2.0", - "resolved": false, + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=", "dev": true, "optional": true @@ -9109,7 +9109,7 @@ }, "readable-stream": { "version": "2.3.6", - "resolved": false, + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz", "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==", "dev": true, "optional": true, @@ -9125,7 +9125,7 @@ }, "rimraf": { "version": "2.6.2", - "resolved": false, + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.6.2.tgz", "integrity": "sha512-lreewLK/BlghmxtfH36YYVg1i8IAce4TI7oao75I1g245+6BctqTVQiBP3YUJ9C6DQOXJmkYR9X9fCLtCOJc5w==", "dev": true, "optional": true, @@ -9135,50 +9135,50 @@ }, "safe-buffer": { "version": "5.1.1", - "resolved": false, - "integrity": "sha512-kKvNJn6Mm93gAczWVJg7wH+wGYWNrDHdWvpUmHyEsgCtIwwo3bqPtV4tR5tuPaUhTOo/kvhVwd8XwwOllGYkbg==", - "dev": true + "bundled": true, + "dev": true, + "optional": true }, "safer-buffer": { "version": "2.1.2", - "resolved": false, + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", "dev": true, "optional": true }, "sax": { "version": "1.2.4", - "resolved": false, + "resolved": "https://registry.npmjs.org/sax/-/sax-1.2.4.tgz", "integrity": "sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw==", "dev": true, "optional": true }, "semver": { "version": "5.5.0", - "resolved": false, + "resolved": "https://registry.npmjs.org/semver/-/semver-5.5.0.tgz", "integrity": "sha512-4SJ3dm0WAwWy/NVeioZh5AntkdJoWKxHxcmyP622fOkgHa4z3R0TdBJICINyaSDE6uNwVc8gZr+ZinwZAH4xIA==", "dev": true, "optional": true }, "set-blocking": { "version": "2.0.0", - "resolved": false, + "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", "integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc=", "dev": true, "optional": true }, "signal-exit": { "version": "3.0.2", - "resolved": false, + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.2.tgz", "integrity": "sha1-tf3AjxKH6hF4Yo5BXiUTK3NkbG0=", "dev": true, "optional": true }, "string-width": { "version": "1.0.2", - "resolved": false, - "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=", + "bundled": true, "dev": true, + "optional": true, "requires": { "code-point-at": "^1.0.0", "is-fullwidth-code-point": "^1.0.0", @@ -9187,7 +9187,7 @@ }, "string_decoder": { "version": "1.1.1", - "resolved": false, + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", "dev": true, "optional": true, @@ -9197,23 +9197,23 @@ }, "strip-ansi": { "version": "3.0.1", - "resolved": false, - "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", + "bundled": true, "dev": true, + "optional": true, "requires": { "ansi-regex": "^2.0.0" } }, "strip-json-comments": { "version": "2.0.1", - "resolved": false, + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", "integrity": "sha1-PFMZQukIwml8DsNEhYwobHygpgo=", "dev": true, "optional": true }, "tar": { "version": "4.4.1", - "resolved": false, + "resolved": "https://registry.npmjs.org/tar/-/tar-4.4.1.tgz", "integrity": "sha512-O+v1r9yN4tOsvl90p5HAP4AEqbYhx4036AGMm075fH9F8Qwi3oJ+v4u50FkT/KkvywNGtwkk0zRI+8eYm1X/xg==", "dev": true, "optional": true, @@ -9229,14 +9229,14 @@ }, "util-deprecate": { "version": "1.0.2", - "resolved": false, + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=", "dev": true, "optional": true }, "wide-align": { "version": "1.1.2", - "resolved": false, + "resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.2.tgz", "integrity": "sha512-ijDLlyQ7s6x1JgCLur53osjm/UXUYD9+0PbYKrBsYisYXzCxN+HC3mYDNy/dWdmf3AwqwU3CXwDCvsNgGK1S0w==", "dev": true, "optional": true, @@ -9246,15 +9246,15 @@ }, "wrappy": { "version": "1.0.2", - "resolved": false, - "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=", - "dev": true + "bundled": true, + "dev": true, + "optional": true }, "yallist": { "version": "3.0.2", - "resolved": false, - "integrity": "sha1-hFK0u36Dx8GI2AQcGoN8dz1ti7k=", - "dev": true + "bundled": true, + "dev": true, + "optional": true } } }, @@ -9363,7 +9363,7 @@ }, "gettext-parser": { "version": "1.1.0", - "resolved": "https://registry.npmjs.org/gettext-parser/-/gettext-parser-1.1.0.tgz", + "resolved": "http://registry.npmjs.org/gettext-parser/-/gettext-parser-1.1.0.tgz", "integrity": "sha1-LFpmONiTk0ubVQN9CtgstwBLJnk=", "dev": true, "requires": { @@ -9455,7 +9455,7 @@ }, "globby": { "version": "6.1.0", - "resolved": "https://registry.npmjs.org/globby/-/globby-6.1.0.tgz", + "resolved": "http://registry.npmjs.org/globby/-/globby-6.1.0.tgz", "integrity": "sha1-9abXDoOV4hyFj7BInWTfAkJNUGw=", "dev": true, "requires": { @@ -9544,7 +9544,7 @@ }, "minimist": { "version": "0.0.10", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.10.tgz", + "resolved": "http://registry.npmjs.org/minimist/-/minimist-0.0.10.tgz", "integrity": "sha1-3j+YVD2/lggr5IrRoMfNqDYwHc8=", "dev": true }, @@ -9732,7 +9732,7 @@ }, "hoist-non-react-statics": { "version": "1.2.0", - "resolved": "https://registry.npmjs.org/hoist-non-react-statics/-/hoist-non-react-statics-1.2.0.tgz", + "resolved": "http://registry.npmjs.org/hoist-non-react-statics/-/hoist-non-react-statics-1.2.0.tgz", "integrity": "sha1-qkSM8JhtVcxAdzsXF0t90GbLfPs=" }, "homedir-polyfill": { @@ -9836,7 +9836,7 @@ }, "http-errors": { "version": "1.6.3", - "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.6.3.tgz", + "resolved": "http://registry.npmjs.org/http-errors/-/http-errors-1.6.3.tgz", "integrity": "sha1-i1VoC7S+KDoLW/TqLjhYC+HZMg0=", "dev": true, "requires": { @@ -10584,7 +10584,7 @@ }, "is-accessor-descriptor": { "version": "0.1.6", - "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz", + "resolved": "http://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz", "integrity": "sha1-qeEss66Nh2cn7u84Q/igiXtcmNY=", "dev": true, "requires": { @@ -10672,7 +10672,7 @@ }, "is-data-descriptor": { "version": "0.1.4", - "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz", + "resolved": "http://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz", "integrity": "sha1-C17mSDiOLIYCgueT8YVv7D8wG1Y=", "dev": true, "requires": { @@ -10777,7 +10777,7 @@ }, "is-obj": { "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-obj/-/is-obj-1.0.1.tgz", + "resolved": "http://registry.npmjs.org/is-obj/-/is-obj-1.0.1.tgz", "integrity": "sha1-PkcprB9f3gJc19g6iW2rn09n2w8=", "dev": true }, @@ -12643,7 +12643,7 @@ }, "magic-string": { "version": "0.22.5", - "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.22.5.tgz", + "resolved": "http://registry.npmjs.org/magic-string/-/magic-string-0.22.5.tgz", "integrity": "sha512-oreip9rJZkzvA8Qzk9HFs8fZGF/u7H/gtrE8EN6RjKJ9kh2HlC+yQ2QezifqTZfGyiuAV0dRv5a+y/8gBb1m9w==", "requires": { "vlq": "^0.2.2" @@ -12766,7 +12766,7 @@ }, "minimist": { "version": "0.0.8", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz", + "resolved": "http://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz", "integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=" }, "quickselect": { @@ -12824,7 +12824,7 @@ }, "mathjs": { "version": "3.20.2", - "resolved": "https://registry.npmjs.org/mathjs/-/mathjs-3.20.2.tgz", + "resolved": "http://registry.npmjs.org/mathjs/-/mathjs-3.20.2.tgz", "integrity": "sha512-3f6/+uf1cUtIz1rYFz775wekl/UEDSQ3mU6xdxW7qzpvvhc2v28i3UtLsGTRB+u8OqDWoSX6Dz8gehaGFs6tCA==", "requires": { "complex.js": "2.0.4", @@ -12874,7 +12874,7 @@ }, "media-typer": { "version": "0.3.0", - "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", + "resolved": "http://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", "integrity": "sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g=", "dev": true }, @@ -13074,7 +13074,7 @@ }, "minimist": { "version": "1.2.0", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", + "resolved": "http://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=" }, "mississippi": { @@ -13126,7 +13126,7 @@ }, "mkdirp": { "version": "0.5.1", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz", + "resolved": "http://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz", "integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=", "requires": { "minimist": "0.0.8" @@ -13134,7 +13134,7 @@ "dependencies": { "minimist": { "version": "0.0.8", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz", + "resolved": "http://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz", "integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=" } } @@ -13801,7 +13801,7 @@ }, "os-tmpdir": { "version": "1.0.2", - "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz", + "resolved": "http://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz", "integrity": "sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ=", "dev": true }, @@ -13988,7 +13988,7 @@ }, "path-browserify": { "version": "0.0.0", - "resolved": "https://registry.npmjs.org/path-browserify/-/path-browserify-0.0.0.tgz", + "resolved": "http://registry.npmjs.org/path-browserify/-/path-browserify-0.0.0.tgz", "integrity": "sha1-oLhwcpquIUAFt9UDLsLLuw+0RRo=", "dev": true }, @@ -14006,7 +14006,7 @@ }, "path-is-absolute": { "version": "1.0.1", - "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "resolved": "http://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=" }, "path-is-inside": { @@ -14147,7 +14147,7 @@ }, "po2json": { "version": "0.4.5", - "resolved": "https://registry.npmjs.org/po2json/-/po2json-0.4.5.tgz", + "resolved": "http://registry.npmjs.org/po2json/-/po2json-0.4.5.tgz", "integrity": "sha1-R7spUtoy1Yob4vJWpZjuvAt0URg=", "dev": true, "requires": { @@ -14209,7 +14209,7 @@ "dependencies": { "async": { "version": "1.5.2", - "resolved": "https://registry.npmjs.org/async/-/async-1.5.2.tgz", + "resolved": "http://registry.npmjs.org/async/-/async-1.5.2.tgz", "integrity": "sha1-7GphrlZIDAw8skHJVhjiCJL5Zyo=", "dev": true } @@ -16377,7 +16377,7 @@ }, "react-ace": { "version": "5.10.0", - "resolved": "https://registry.npmjs.org/react-ace/-/react-ace-5.10.0.tgz", + "resolved": "http://registry.npmjs.org/react-ace/-/react-ace-5.10.0.tgz", "integrity": "sha512-aEK/XZCowP8IXq91e2DYqOtGhabk1bbjt+fyeW0UBcIkzDzP/RX/MeJKeyW7wsZcwElACVwyy9nnwXBTqgky3A==", "requires": { "brace": "^0.11.0", @@ -16456,7 +16456,7 @@ }, "react-dnd": { "version": "2.6.0", - "resolved": "https://registry.npmjs.org/react-dnd/-/react-dnd-2.6.0.tgz", + "resolved": "http://registry.npmjs.org/react-dnd/-/react-dnd-2.6.0.tgz", "integrity": "sha1-f6JWds+CfViokSk+PBq1naACVFo=", "requires": { "disposables": "^1.0.1", @@ -16476,7 +16476,7 @@ }, "react-dnd-html5-backend": { "version": "2.6.0", - "resolved": "https://registry.npmjs.org/react-dnd-html5-backend/-/react-dnd-html5-backend-2.6.0.tgz", + "resolved": "http://registry.npmjs.org/react-dnd-html5-backend/-/react-dnd-html5-backend-2.6.0.tgz", "integrity": "sha1-WQzRzKeEQbsnTt1XH+9MCxbdz44=", "requires": { "lodash": "^4.2.0" @@ -16924,7 +16924,7 @@ }, "readable-stream": { "version": "2.3.6", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz", + "resolved": "http://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz", "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==", "requires": { "core-util-is": "~1.0.0", @@ -17275,7 +17275,7 @@ }, "reduce-css-calc": { "version": "1.3.0", - "resolved": "https://registry.npmjs.org/reduce-css-calc/-/reduce-css-calc-1.3.0.tgz", + "resolved": "http://registry.npmjs.org/reduce-css-calc/-/reduce-css-calc-1.3.0.tgz", "integrity": "sha1-dHyRTgSWFKTJz7umKYca0dKSdxY=", "requires": { "balanced-match": "^0.4.2", @@ -17398,7 +17398,7 @@ }, "regexpp": { "version": "1.1.0", - "resolved": "https://registry.npmjs.org/regexpp/-/regexpp-1.1.0.tgz", + "resolved": "http://registry.npmjs.org/regexpp/-/regexpp-1.1.0.tgz", "integrity": "sha512-LOPw8FpgdQF9etWMaAfG/WRthIdXJGYp4mJ2Jgn/2lpkbod9jPn0t9UqN7AxBOKNfzRbYyVfgc7Vk4t/MpnXgw==", "dev": true }, @@ -17430,7 +17430,7 @@ "dependencies": { "jsesc": { "version": "0.5.0", - "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-0.5.0.tgz", + "resolved": "http://registry.npmjs.org/jsesc/-/jsesc-0.5.0.tgz", "integrity": "sha1-597mbjXW/Bb3EP6R1c9p9w8IkR0=", "dev": true } @@ -17546,7 +17546,7 @@ }, "require-uncached": { "version": "1.0.3", - "resolved": "https://registry.npmjs.org/require-uncached/-/require-uncached-1.0.3.tgz", + "resolved": "http://registry.npmjs.org/require-uncached/-/require-uncached-1.0.3.tgz", "integrity": "sha1-Tg1W1slmL9MeQwEcS5WqSZVUIdM=", "dev": true, "requires": { @@ -17629,7 +17629,7 @@ }, "rgba-regex": { "version": "1.0.0", - "resolved": "https://registry.npmjs.org/rgba-regex/-/rgba-regex-1.0.0.tgz", + "resolved": "http://registry.npmjs.org/rgba-regex/-/rgba-regex-1.0.0.tgz", "integrity": "sha1-QzdOLiyglosO8VI0YLfXMP8i7rM=", "dev": true }, @@ -17713,7 +17713,7 @@ }, "safe-regex": { "version": "1.1.0", - "resolved": "https://registry.npmjs.org/safe-regex/-/safe-regex-1.1.0.tgz", + "resolved": "http://registry.npmjs.org/safe-regex/-/safe-regex-1.1.0.tgz", "integrity": "sha1-QKNmnzsHfR6UPURinhV91IAjvy4=", "dev": true, "requires": { @@ -17891,7 +17891,7 @@ }, "sha.js": { "version": "2.4.11", - "resolved": "https://registry.npmjs.org/sha.js/-/sha.js-2.4.11.tgz", + "resolved": "http://registry.npmjs.org/sha.js/-/sha.js-2.4.11.tgz", "integrity": "sha512-QMEp5B7cftE7APOjk5Y6xgrbWu+WkLVQwk8JNjZ8nKRciZaByEW6MubieAiToS7+dwvrjGhH8jRXz3MVd0AYqQ==", "dev": true, "requires": { @@ -17926,7 +17926,7 @@ }, "iconv-lite": { "version": "0.2.11", - "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.2.11.tgz", + "resolved": "http://registry.npmjs.org/iconv-lite/-/iconv-lite-0.2.11.tgz", "integrity": "sha1-HOYKOleGSiktEyH/RgnKS7llrcg=" } } @@ -17946,7 +17946,7 @@ "dependencies": { "minimist": { "version": "0.0.5", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.5.tgz", + "resolved": "http://registry.npmjs.org/minimist/-/minimist-0.0.5.tgz", "integrity": "sha1-16oye87PUY+RBqxrjwA/o7zqhWY=" } } @@ -18005,7 +18005,7 @@ }, "sinon": { "version": "4.5.0", - "resolved": "https://registry.npmjs.org/sinon/-/sinon-4.5.0.tgz", + "resolved": "http://registry.npmjs.org/sinon/-/sinon-4.5.0.tgz", "integrity": "sha512-trdx+mB0VBBgoYucy6a9L7/jfQOmvGeaKZT4OOJ+lPAtI8623xyGr8wLiE4eojzBS8G9yXbhx42GHUOVLr4X2w==", "dev": true, "requires": { @@ -18397,7 +18397,7 @@ }, "split": { "version": "0.2.10", - "resolved": "https://registry.npmjs.org/split/-/split-0.2.10.tgz", + "resolved": "http://registry.npmjs.org/split/-/split-0.2.10.tgz", "integrity": "sha1-Zwl8YB1pfOE2j0GPBs0gHPBSGlc=", "requires": { "through": "2" @@ -18440,7 +18440,7 @@ }, "sprintf-js": { "version": "1.0.3", - "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", + "resolved": "http://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", "integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=", "dev": true }, @@ -18692,7 +18692,7 @@ }, "string_decoder": { "version": "1.1.1", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "resolved": "http://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", "requires": { "safe-buffer": "~5.1.0" @@ -18700,7 +18700,7 @@ }, "strip-ansi": { "version": "3.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", + "resolved": "http://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", "dev": true, "requires": { @@ -18715,7 +18715,7 @@ }, "strip-eof": { "version": "1.0.0", - "resolved": "https://registry.npmjs.org/strip-eof/-/strip-eof-1.0.0.tgz", + "resolved": "http://registry.npmjs.org/strip-eof/-/strip-eof-1.0.0.tgz", "integrity": "sha1-u0P/VZim6wXYm1n80SnJgzE2Br8=", "dev": true }, @@ -18960,7 +18960,7 @@ }, "fast-deep-equal": { "version": "1.1.0", - "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-1.1.0.tgz", + "resolved": "http://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-1.1.0.tgz", "integrity": "sha1-wFNHeBfIa1HaqFPIHgWbcz0CNhQ=", "dev": true }, @@ -19017,7 +19017,7 @@ }, "tapable": { "version": "0.1.10", - "resolved": "https://registry.npmjs.org/tapable/-/tapable-0.1.10.tgz", + "resolved": "http://registry.npmjs.org/tapable/-/tapable-0.1.10.tgz", "integrity": "sha1-KcNXB8K3DlDQdIK10gLo7URtr9Q=", "dev": true }, @@ -19205,7 +19205,7 @@ }, "through": { "version": "2.3.8", - "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz", + "resolved": "http://registry.npmjs.org/through/-/through-2.3.8.tgz", "integrity": "sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU=" }, "through2": { @@ -19901,7 +19901,7 @@ }, "tty-browserify": { "version": "0.0.0", - "resolved": "https://registry.npmjs.org/tty-browserify/-/tty-browserify-0.0.0.tgz", + "resolved": "http://registry.npmjs.org/tty-browserify/-/tty-browserify-0.0.0.tgz", "integrity": "sha1-oVe6QC2iTpv5V/mqadUk7tQpAaY=", "dev": true }, @@ -20434,7 +20434,7 @@ }, "vm-browserify": { "version": "0.0.4", - "resolved": "https://registry.npmjs.org/vm-browserify/-/vm-browserify-0.0.4.tgz", + "resolved": "http://registry.npmjs.org/vm-browserify/-/vm-browserify-0.0.4.tgz", "integrity": "sha1-XX6kW7755Kb/ZflUOOCofDV9WnM=", "dev": true, "requires": { @@ -22016,7 +22016,7 @@ }, "wrap-ansi": { "version": "2.1.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-2.1.0.tgz", + "resolved": "http://registry.npmjs.org/wrap-ansi/-/wrap-ansi-2.1.0.tgz", "integrity": "sha1-2Pw9KE3QV5T+hJc8rs3Rz4JP3YU=", "dev": true, "requires": { @@ -22101,7 +22101,7 @@ }, "xmlbuilder": { "version": "9.0.7", - "resolved": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-9.0.7.tgz", + "resolved": "http://registry.npmjs.org/xmlbuilder/-/xmlbuilder-9.0.7.tgz", "integrity": "sha1-Ey7mPS7FVlxVfiD0wi35rKaGsQ0=" }, "xregexp": { diff --git a/superset/assets/spec/javascripts/chart/chartActions_spec.js b/superset/assets/spec/javascripts/chart/chartActions_spec.js index 09f618c6c..a4a832632 100644 --- a/superset/assets/spec/javascripts/chart/chartActions_spec.js +++ b/superset/assets/spec/javascripts/chart/chartActions_spec.js @@ -51,7 +51,7 @@ describe('chart actions', () => { }); it('should dispatch CHART_UPDATE_STARTED action before the query', () => { - const actionThunk = actions.runQuery({}); + const actionThunk = actions.postChartFormData({}); return actionThunk(dispatch).then(() => { // chart update, trigger query, update form data, success @@ -64,7 +64,7 @@ describe('chart actions', () => { }); it('should dispatch TRIGGER_QUERY action with the query', () => { - const actionThunk = actions.runQuery({}); + const actionThunk = actions.postChartFormData({}); return actionThunk(dispatch).then(() => { // chart update, trigger query, update form data, success expect(dispatch.callCount).toBe(5); @@ -76,7 +76,7 @@ describe('chart actions', () => { }); it('should dispatch UPDATE_QUERY_FORM_DATA action with the query', () => { - const actionThunk = actions.runQuery({}); + const actionThunk = actions.postChartFormData({}); return actionThunk(dispatch).then(() => { // chart update, trigger query, update form data, success expect(dispatch.callCount).toBe(5); @@ -88,7 +88,7 @@ describe('chart actions', () => { }); it('should dispatch logEvent async action', () => { - const actionThunk = actions.runQuery({}); + const actionThunk = actions.postChartFormData({}); return actionThunk(dispatch).then(() => { // chart update, trigger query, update form data, success expect(dispatch.callCount).toBe(5); @@ -104,7 +104,7 @@ describe('chart actions', () => { }); it('should dispatch CHART_UPDATE_SUCCEEDED action upon success', () => { - const actionThunk = actions.runQuery({}); + const actionThunk = actions.postChartFormData({}); return actionThunk(dispatch).then(() => { // chart update, trigger query, update form data, success expect(dispatch.callCount).toBe(5); @@ -120,7 +120,7 @@ describe('chart actions', () => { fetchMock.post(MOCK_URL, () => unresolvingPromise, { overwriteRoutes: true }); const timeoutInSec = 1 / 1000; - const actionThunk = actions.runQuery({}, false, timeoutInSec); + const actionThunk = actions.postChartFormData({}, false, timeoutInSec); return actionThunk(dispatch).then(() => { // chart update, trigger query, update form data, fail @@ -136,7 +136,7 @@ describe('chart actions', () => { fetchMock.post(MOCK_URL, { throws: { statusText: 'misc error' } }, { overwriteRoutes: true }); const timeoutInSec = 1 / 1000; - const actionThunk = actions.runQuery({}, false, timeoutInSec); + const actionThunk = actions.postChartFormData({}, false, timeoutInSec); return actionThunk(dispatch).then(() => { // chart update, trigger query, update form data, fail diff --git a/superset/assets/spec/javascripts/dashboard/components/Dashboard_spec.jsx b/superset/assets/spec/javascripts/dashboard/components/Dashboard_spec.jsx index 523b835d4..de637cd2a 100644 --- a/superset/assets/spec/javascripts/dashboard/components/Dashboard_spec.jsx +++ b/superset/assets/spec/javascripts/dashboard/components/Dashboard_spec.jsx @@ -39,7 +39,7 @@ describe('Dashboard', () => { actions: { addSliceToDashboard() {}, removeSliceFromDashboard() {}, - runQuery() {}, + postChartFormData() {}, logEvent() {}, }, initMessages: [], @@ -82,15 +82,15 @@ describe('Dashboard', () => { }, }; - it('should call runQuery for all non-exempt slices', () => { + it('should call postChartFormData for all non-exempt slices', () => { const wrapper = setup({ charts: overrideCharts, slices: overrideSlices }); - const spy = sinon.spy(props.actions, 'runQuery'); + const spy = sinon.spy(props.actions, 'postChartFormData'); wrapper.instance().refreshExcept('1001'); spy.restore(); expect(spy.callCount).toBe(Object.keys(overrideCharts).length - 1); }); - it('should not call runQuery for filter_immune_slices', () => { + it('should not call postChartFormData for filter_immune_slices', () => { const wrapper = setup({ charts: overrideCharts, dashboardInfo: { @@ -103,7 +103,7 @@ describe('Dashboard', () => { }, }, }); - const spy = sinon.spy(props.actions, 'runQuery'); + const spy = sinon.spy(props.actions, 'postChartFormData'); wrapper.instance().refreshExcept(); spy.restore(); expect(spy.callCount).toBe(0); diff --git a/superset/assets/src/chart/Chart.jsx b/superset/assets/src/chart/Chart.jsx index bc19d63b6..8cb615361 100644 --- a/superset/assets/src/chart/Chart.jsx +++ b/superset/assets/src/chart/Chart.jsx @@ -68,12 +68,23 @@ class Chart extends React.PureComponent { } componentDidMount() { if (this.props.triggerQuery) { - this.props.actions.runQuery( - this.props.formData, - false, - this.props.timeout, - this.props.chartId, - ); + if (this.props.chartId > 0) { + // Load saved chart with a GET request + this.props.actions.getSavedChart( + this.props.formData, + false, + this.props.timeout, + this.props.chartId, + ); + } else { + // Create chart with POST request + this.props.actions.postChartFormData( + this.props.formData, + false, + this.props.timeout, + this.props.chartId, + ); + } } } diff --git a/superset/assets/src/chart/chartAction.js b/superset/assets/src/chart/chartAction.js index 93433c823..3909dadd8 100644 --- a/superset/assets/src/chart/chartAction.js +++ b/superset/assets/src/chart/chartAction.js @@ -165,14 +165,14 @@ export function addChart(chart, key) { return { type: ADD_CHART, chart, key }; } -export const RUN_QUERY = 'RUN_QUERY'; -export function runQuery(formData, force = false, timeout = 60, key) { +export function exploreJSON(formData, force = false, timeout = 60, key, method) { return (dispatch) => { const { url, payload } = getExploreUrlAndPayload({ formData, endpointType: 'json', force, allowDomainSharding: true, + method, }); const logStart = Logger.getTimestamp(); const controller = new AbortController(); @@ -193,7 +193,9 @@ export function runQuery(formData, force = false, timeout = 60, key) { credentials: 'include', }; } - const queryPromise = SupersetClient.post(querySettings) + + const clientMethod = method === 'GET' ? SupersetClient.get : SupersetClient.post; + const queryPromise = clientMethod(querySettings) .then(({ json }) => { dispatch(logEvent(LOG_ACTIONS_LOAD_CHART, { slice_id: key, @@ -246,6 +248,32 @@ export function runQuery(formData, force = false, timeout = 60, key) { }; } +export const GET_SAVED_CHART = 'GET_SAVED_CHART'; +export function getSavedChart(formData, force = false, timeout = 60, key) { + /* + * Perform a GET request to `/explore_json`. + * + * This will return the payload of a saved chart, optionally filtered by + * ad-hoc or extra filters from dashboards. Eg: + * + * GET /explore_json?{"chart_id":1} + * GET /explore_json?{"chart_id":1,"extra_filters":"..."} + * + */ + return exploreJSON(formData, force, timeout, key, 'GET'); +} + +export const POST_CHART_FORM_DATA = 'POST_CHART_FORM_DATA'; +export function postChartFormData(formData, force = false, timeout = 60, key) { + /* + * Perform a POST request to `/explore_json`. + * + * This will post the form data to the endpoint, returning a new chart. + * + */ + return exploreJSON(formData, force, timeout, key, 'POST'); +} + export function redirectSQLLab(formData) { return (dispatch) => { const { url } = getExploreUrlAndPayload({ formData, endpointType: 'query' }); @@ -272,6 +300,6 @@ export function refreshChart(chart, force, timeout) { if (!chart.latestQueryFormData || Object.keys(chart.latestQueryFormData).length === 0) { return; } - dispatch(runQuery(chart.latestQueryFormData, force, timeout, chart.id)); + dispatch(postChartFormData(chart.latestQueryFormData, force, timeout, chart.id)); }; } diff --git a/superset/assets/src/dashboard/components/Dashboard.jsx b/superset/assets/src/dashboard/components/Dashboard.jsx index dc1f05479..32cc3768c 100644 --- a/superset/assets/src/dashboard/components/Dashboard.jsx +++ b/superset/assets/src/dashboard/components/Dashboard.jsx @@ -40,7 +40,7 @@ const propTypes = { actions: PropTypes.shape({ addSliceToDashboard: PropTypes.func.isRequired, removeSliceFromDashboard: PropTypes.func.isRequired, - runQuery: PropTypes.func.isRequired, + postChartFormData: PropTypes.func.isRequired, logEvent: PropTypes.func.isRequired, }).isRequired, dashboardInfo: dashboardInfoPropShape.isRequired, @@ -156,7 +156,7 @@ class Dashboard extends React.PureComponent { sliceId: chart.id, }); - this.props.actions.runQuery( + this.props.actions.postChartFormData( updatedFormData, false, this.props.timeout, diff --git a/superset/assets/src/dashboard/containers/Dashboard.jsx b/superset/assets/src/dashboard/containers/Dashboard.jsx index 865ec40ac..e5cf4fb59 100644 --- a/superset/assets/src/dashboard/containers/Dashboard.jsx +++ b/superset/assets/src/dashboard/containers/Dashboard.jsx @@ -25,7 +25,7 @@ import { addSliceToDashboard, removeSliceFromDashboard, } from '../actions/dashboardState'; -import { runQuery } from '../../chart/chartAction'; +import { postChartFormData } from '../../chart/chartAction'; import { logEvent } from '../../logger/actions'; import getLoadStatsPerTopLevelComponent from '../util/logging/getLoadStatsPerTopLevelComponent'; @@ -64,7 +64,7 @@ function mapDispatchToProps(dispatch) { { addSliceToDashboard, removeSliceFromDashboard, - runQuery, + postChartFormData, logEvent, }, dispatch, diff --git a/superset/assets/src/explore/components/ExploreChartHeader.jsx b/superset/assets/src/explore/components/ExploreChartHeader.jsx index d2f3b9830..31c74ad1a 100644 --- a/superset/assets/src/explore/components/ExploreChartHeader.jsx +++ b/superset/assets/src/explore/components/ExploreChartHeader.jsx @@ -50,8 +50,8 @@ const propTypes = { }; class ExploreChartHeader extends React.PureComponent { - runQuery() { - this.props.actions.runQuery(this.props.form_data, true, + postChartFormData() { + this.props.actions.postChartFormData(this.props.form_data, true, this.props.timeout, this.props.chart.id); } @@ -142,7 +142,7 @@ class ExploreChartHeader extends React.PureComponent { />} {chartSucceeded && queryResponse && queryResponse.is_cached && } /') def slice(self, slice_id): - form_data, slc = self.get_form_data(slice_id, use_slice_data=True) + form_data, slc = get_form_data(slice_id, use_slice_data=True) if not slc: abort(404) endpoint = '/superset/explore/?form_data={}'.format( @@ -1138,18 +1100,7 @@ class Superset(BaseSupersetView): }) def generate_json( - self, datasource_type, datasource_id, form_data, - csv=False, query=False, force=False, results=False, - samples=False, - ): - viz_obj = self.get_viz( - datasource_type=datasource_type, - datasource_id=datasource_id, - form_data=form_data, - force=force, - ) - security_manager.assert_datasource_permission(viz_obj.datasource) - + self, viz_obj, csv=False, query=False, results=False, samples=False): if csv: return CsvResponse( viz_obj.get_csv(), @@ -1173,21 +1124,25 @@ class Superset(BaseSupersetView): @api @has_access_api @expose('/slice_json/') + @etag_cache(CACHE_DEFAULT_TIMEOUT, check_perms=check_slice_perms) def slice_json(self, slice_id): - form_data, slc = self.get_form_data(slice_id, use_slice_data=True) + form_data, slc = get_form_data(slice_id, use_slice_data=True) datasource_type = slc.datasource.type datasource_id = slc.datasource.id - - return self.generate_json(datasource_type=datasource_type, - datasource_id=datasource_id, - form_data=form_data) + viz_obj = get_viz( + datasource_type=datasource_type, + datasource_id=datasource_id, + form_data=form_data, + force=False, + ) + return self.generate_json(viz_obj) @log_this @api @has_access_api @expose('/annotation_json/') def annotation_json(self, layer_id): - form_data = self.get_form_data()[0] + form_data = get_form_data()[0] form_data['layer_id'] = layer_id form_data['filters'] = [{'col': 'layer_id', 'op': '==', @@ -1207,6 +1162,7 @@ class Superset(BaseSupersetView): @handle_api_exception @expose('/explore_json///', methods=['GET', 'POST']) @expose('/explore_json/', methods=['GET', 'POST']) + @etag_cache(CACHE_DEFAULT_TIMEOUT, check_perms=check_datasource_perms) def explore_json(self, datasource_type=None, datasource_id=None): """Serves all request that GET or POST form_data @@ -1223,18 +1179,21 @@ class Superset(BaseSupersetView): samples = request.args.get('samples') == 'true' force = request.args.get('force') == 'true' - form_data = self.get_form_data()[0] - datasource_id, datasource_type = self.datasource_info( + form_data = get_form_data()[0] + datasource_id, datasource_type = get_datasource_info( datasource_id, datasource_type, form_data) - - return self.generate_json( + viz_obj = get_viz( datasource_type=datasource_type, datasource_id=datasource_id, form_data=form_data, + force=force, + ) + + return self.generate_json( + viz_obj, csv=csv, query=query, results=results, - force=force, samples=samples, ) @@ -1260,34 +1219,15 @@ class Superset(BaseSupersetView): datasource_id=datasource_id, **request.args)) - @staticmethod - def datasource_info(datasource_id, datasource_type, form_data): - """Compatibility layer for handling of datasource info - - datasource_id & datasource_type used to be passed in the URL - directory, now they should come as part of the form_data, - This function allows supporting both without duplicating code""" - datasource = form_data.get('datasource', '') - if '__' in datasource: - datasource_id, datasource_type = datasource.split('__') - # The case where the datasource has been deleted - datasource_id = None if datasource_id == 'None' else datasource_id - - if not datasource_id: - raise Exception( - 'The datasource associated with this chart no longer exists') - datasource_id = int(datasource_id) - return datasource_id, datasource_type - @log_this @has_access @expose('/explore///', methods=['GET', 'POST']) @expose('/explore/', methods=['GET', 'POST']) def explore(self, datasource_type=None, datasource_id=None): user_id = g.user.get_id() if g.user else None - form_data, slc = self.get_form_data(use_slice_data=True) + form_data, slc = get_form_data(use_slice_data=True) - datasource_id, datasource_type = self.datasource_info( + datasource_id, datasource_type = get_datasource_info( datasource_id, datasource_type, form_data) error_redirect = '/chart/list/' @@ -1413,7 +1353,7 @@ class Superset(BaseSupersetView): """Save or overwrite a slice""" slice_name = args.get('slice_name') action = args.get('action') - form_data, unused_slc = self.get_form_data() + form_data = get_form_data()[0] if action in ('saveas'): if 'slice_id' in form_data: @@ -2105,8 +2045,8 @@ class Superset(BaseSupersetView): for slc in slices: try: - form_data = self.get_form_data(slc.id, use_slice_data=True)[0] - obj = self.get_viz( + form_data = get_form_data(slc.id, use_slice_data=True)[0] + obj = get_viz( datasource_type=slc.datasource.type, datasource_id=slc.datasource.id, form_data=form_data, @@ -2874,7 +2814,7 @@ class Superset(BaseSupersetView): This method exposes an API endpoint to get the database query string for this slice """ - viz_obj = self.get_viz(slice_id) + viz_obj = get_viz(slice_id) security_manager.assert_datasource_permission(viz_obj.datasource) return self.get_query_string_response(viz_obj) diff --git a/superset/views/utils.py b/superset/views/utils.py index eb68316ba..4318141e3 100644 --- a/superset/views/utils.py +++ b/superset/views/utils.py @@ -16,11 +16,25 @@ # under the License. # pylint: disable=C,R,W from collections import defaultdict +from urllib import parse -from flask import g +from flask import g, request from flask_appbuilder.security.sqla import models as ab_models +import simplejson as json -from superset import db +from superset import app, db, viz +from superset.connectors.connector_registry import ConnectorRegistry +from superset.legacy import cast_form_data, update_time_range +import superset.models.core as models + + +FORM_DATA_KEY_BLACKLIST = [] +if not app.config.get('ENABLE_JAVASCRIPT_CONTROLS'): + FORM_DATA_KEY_BLACKLIST = [ + 'js_tooltip', + 'js_onclick_href', + 'js_data_mutator', + ] def bootstrap_user_data(username=None, include_perms=False): @@ -74,3 +88,103 @@ def get_permissions(user): ] return roles, permissions + + +def get_viz( + slice_id=None, + form_data=None, + datasource_type=None, + datasource_id=None, + force=False, +): + if slice_id: + slc = ( + db.session.query(models.Slice) + .filter_by(id=slice_id) + .one() + ) + return slc.get_viz() + else: + viz_type = form_data.get('viz_type', 'table') + datasource = ConnectorRegistry.get_datasource( + datasource_type, datasource_id, db.session) + viz_obj = viz.viz_types[viz_type]( + datasource, + form_data=form_data, + force=force, + ) + return viz_obj + + +def get_form_data(slice_id=None, use_slice_data=False): + form_data = {} + post_data = request.form.get('form_data') + request_args_data = request.args.get('form_data') + # Supporting POST + if post_data: + form_data.update(json.loads(post_data)) + # request params can overwrite post body + if request_args_data: + form_data.update(json.loads(request_args_data)) + + url_id = request.args.get('r') + if url_id: + saved_url = db.session.query(models.Url).filter_by(id=url_id).first() + if saved_url: + url_str = parse.unquote_plus( + saved_url.url.split('?')[1][10:], encoding='utf-8', errors=None) + url_form_data = json.loads(url_str) + # allow form_date in request override saved url + url_form_data.update(form_data) + form_data = url_form_data + + if request.args.get('viz_type'): + # Converting old URLs + form_data = cast_form_data(form_data) + + form_data = { + k: v + for k, v in form_data.items() + if k not in FORM_DATA_KEY_BLACKLIST + } + + # When a slice_id is present, load from DB and override + # the form_data from the DB with the other form_data provided + slice_id = form_data.get('slice_id') or slice_id + slc = None + + # Check if form data only contains slice_id and additional filters + valid_keys = ['slice_id', 'extra_filters', 'adhoc_filters'] + valid_slice_id = all(key in valid_keys for key in form_data) + + # Include the slice_form_data if request from explore or slice calls + # or if form_data only contains slice_id and additional filters + if slice_id and (use_slice_data or valid_slice_id): + slc = db.session.query(models.Slice).filter_by(id=slice_id).one_or_none() + if slc: + slice_form_data = slc.form_data.copy() + slice_form_data.update(form_data) + form_data = slice_form_data + + update_time_range(form_data) + + return form_data, slc + + +def get_datasource_info(datasource_id, datasource_type, form_data): + """Compatibility layer for handling of datasource info + + datasource_id & datasource_type used to be passed in the URL + directory, now they should come as part of the form_data, + This function allows supporting both without duplicating code""" + datasource = form_data.get('datasource', '') + if '__' in datasource: + datasource_id, datasource_type = datasource.split('__') + # The case where the datasource has been deleted + datasource_id = None if datasource_id == 'None' else datasource_id + + if not datasource_id: + raise Exception( + 'The datasource associated with this chart no longer exists') + datasource_id = int(datasource_id) + return datasource_id, datasource_type From 6998b1014a2341af51d8720a4a371fc6a2042a6e Mon Sep 17 00:00:00 2001 From: Beto Dealmeida Date: Wed, 3 Apr 2019 12:43:39 -0700 Subject: [PATCH 13/38] Allow bigint to be used as time column in Presto (#7218) --- superset/db_engine_specs.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/superset/db_engine_specs.py b/superset/db_engine_specs.py index 8be1a216d..9eee7c52f 100644 --- a/superset/db_engine_specs.py +++ b/superset/db_engine_specs.py @@ -832,6 +832,8 @@ class PrestoEngineSpec(BaseEngineSpec): return "from_iso8601_date('{}')".format(dttm.isoformat()[:10]) if tt == 'TIMESTAMP': return "from_iso8601_timestamp('{}')".format(dttm.isoformat()) + if tt == 'BIGINT': + return "to_unixtime(from_iso8601_timestamp('{}'))".format(dttm.isoformat()) return "'{}'".format(dttm.strftime('%Y-%m-%d %H:%M:%S')) @classmethod From 1132c3cbde8db11168c6852d7c7775717ac0dba2 Mon Sep 17 00:00:00 2001 From: Kim Truong <47833996+khtruong@users.noreply.github.com> Date: Wed, 3 Apr 2019 13:35:27 -0700 Subject: [PATCH 14/38] feat: [lyftga] add tooltips to deck.gl viz types (#7206) * Add tooltips to deck.gl viz types * Handle text overflow * Use .css file instead of .less * fix: add tooltip component * Add tooltips to deck.gl viz types * Handle text overflow * Use .css file instead of .less * fix: add tooltip component * fix: casing, types, and keys --- .../visualizations/deckgl/DeckGLContainer.jsx | 1 + .../src/visualizations/deckgl/TooltipRow.jsx | 36 +++++++++++++++++++ .../visualizations/deckgl/layers/Arc/Arc.jsx | 17 ++++++++- .../deckgl/layers/Geojson/Geojson.jsx | 16 ++++++++- .../deckgl/layers/Grid/Grid.jsx | 14 +++++++- .../visualizations/deckgl/layers/Hex/Hex.jsx | 14 +++++++- .../deckgl/layers/Path/Path.jsx | 17 ++++++++- .../deckgl/layers/Polygon/Polygon.jsx | 15 +++++++- .../deckgl/layers/Scatter/Scatter.jsx | 19 +++++++++- .../deckgl/layers/Screengrid/Screengrid.jsx | 13 ++++++- .../visualizations/deckgl/layers/common.jsx | 14 ++------ .../src/visualizations/stylesheets/deckgl.css | 22 ++++++++++++ 12 files changed, 178 insertions(+), 20 deletions(-) create mode 100644 superset/assets/src/visualizations/deckgl/TooltipRow.jsx create mode 100644 superset/assets/src/visualizations/stylesheets/deckgl.css diff --git a/superset/assets/src/visualizations/deckgl/DeckGLContainer.jsx b/superset/assets/src/visualizations/deckgl/DeckGLContainer.jsx index c10f16ac9..ff414bfc0 100644 --- a/superset/assets/src/visualizations/deckgl/DeckGLContainer.jsx +++ b/superset/assets/src/visualizations/deckgl/DeckGLContainer.jsx @@ -22,6 +22,7 @@ import MapGL from 'react-map-gl'; import DeckGL from 'deck.gl'; import 'mapbox-gl/dist/mapbox-gl.css'; import { isEqual } from 'lodash'; +import '../stylesheets/deckgl.css'; const TICK = 2000; // milliseconds diff --git a/superset/assets/src/visualizations/deckgl/TooltipRow.jsx b/superset/assets/src/visualizations/deckgl/TooltipRow.jsx new file mode 100644 index 000000000..cc85bfd98 --- /dev/null +++ b/superset/assets/src/visualizations/deckgl/TooltipRow.jsx @@ -0,0 +1,36 @@ +/** + * 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'; + +const propTypes = { + label: PropTypes.string.isRequired, + value: PropTypes.string.isRequired, +}; + + +export default class TooltipRow extends React.PureComponent { + render() { + return ( +
{this.props.label}{this.props.value}
+ ); + } +} + +TooltipRow.propTypes = propTypes; diff --git a/superset/assets/src/visualizations/deckgl/layers/Arc/Arc.jsx b/superset/assets/src/visualizations/deckgl/layers/Arc/Arc.jsx index b6adbbfbf..2c0a99b09 100644 --- a/superset/assets/src/visualizations/deckgl/layers/Arc/Arc.jsx +++ b/superset/assets/src/visualizations/deckgl/layers/Arc/Arc.jsx @@ -17,8 +17,11 @@ * under the License. */ import { ArcLayer } from 'deck.gl'; +import React from 'react'; +import { t } from '@superset-ui/translation'; import { commonLayerProps } from '../common'; import { createCategoricalDeckGLComponent } from '../../factory'; +import TooltipRow from '../../TooltipRow'; function getPoints(data) { const points = []; @@ -29,6 +32,18 @@ function getPoints(data) { return points; } +function setTooltipContent(formData) { + return o => ( +
+ + + { + formData.dimension && + } +
+ ); +} + export function getLayer(fd, payload, onAddFilter, setTooltip) { const data = payload.data.features; const sc = fd.color_picker; @@ -39,7 +54,7 @@ export function getLayer(fd, payload, onAddFilter, setTooltip) { getSourceColor: d => d.sourceColor || d.color || [sc.r, sc.g, sc.b, 255 * sc.a], getTargetColor: d => d.targetColor || d.color || [tc.r, tc.g, tc.b, 255 * tc.a], strokeWidth: (fd.stroke_width) ? fd.stroke_width : 3, - ...commonLayerProps(fd, setTooltip), + ...commonLayerProps(fd, setTooltip, setTooltipContent(fd)), }); } diff --git a/superset/assets/src/visualizations/deckgl/layers/Geojson/Geojson.jsx b/superset/assets/src/visualizations/deckgl/layers/Geojson/Geojson.jsx index 1dd3cdb47..7488a3ddf 100644 --- a/superset/assets/src/visualizations/deckgl/layers/Geojson/Geojson.jsx +++ b/superset/assets/src/visualizations/deckgl/layers/Geojson/Geojson.jsx @@ -25,6 +25,7 @@ import DeckGLContainer from '../../DeckGLContainer'; import { hexToRGB } from '../../../../modules/colors'; import sandboxedEval from '../../../../modules/sandbox'; import { commonLayerProps } from '../common'; +import TooltipRow from '../../TooltipRow'; const propertyMap = { fillColor: 'fillColor', @@ -75,6 +76,19 @@ const recurseGeoJson = (node, propOverrides, extraProps) => { } }; +function setTooltipContent(o) { + return ( + o.object.extraProps && +
+ { + Object.keys(o.object.extraProps).map((prop, index) => + , + ) + } +
+ ); +} + export function getLayer(formData, payload, onAddFilter, setTooltip) { const fd = formData; const fc = fd.fill_color_picker; @@ -106,7 +120,7 @@ export function getLayer(formData, payload, onAddFilter, setTooltip) { stroked: fd.stroked, extruded: fd.extruded, pointRadiusScale: fd.point_radius_scale, - ...commonLayerProps(fd, setTooltip), + ...commonLayerProps(fd, setTooltip, setTooltipContent), }); } diff --git a/superset/assets/src/visualizations/deckgl/layers/Grid/Grid.jsx b/superset/assets/src/visualizations/deckgl/layers/Grid/Grid.jsx index 67c4bfd83..a0cc8613f 100644 --- a/superset/assets/src/visualizations/deckgl/layers/Grid/Grid.jsx +++ b/superset/assets/src/visualizations/deckgl/layers/Grid/Grid.jsx @@ -17,10 +17,22 @@ * under the License. */ import { GridLayer } from 'deck.gl'; +import React from 'react'; +import { t } from '@superset-ui/translation'; import { commonLayerProps, getAggFunc } from '../common'; import sandboxedEval from '../../../../modules/sandbox'; import { createDeckGLComponent } from '../../factory'; +import TooltipRow from '../../TooltipRow'; + +function setTooltipContent(o) { + return ( +
+ + +
+ ); +} export function getLayer(formData, payload, onAddFilter, setTooltip) { const fd = formData; @@ -48,7 +60,7 @@ export function getLayer(formData, payload, onAddFilter, setTooltip) { outline: false, getElevationValue: aggFunc, getColorValue: aggFunc, - ...commonLayerProps(fd, setTooltip), + ...commonLayerProps(fd, setTooltip, setTooltipContent), }); } diff --git a/superset/assets/src/visualizations/deckgl/layers/Hex/Hex.jsx b/superset/assets/src/visualizations/deckgl/layers/Hex/Hex.jsx index f8562254e..9901b2262 100644 --- a/superset/assets/src/visualizations/deckgl/layers/Hex/Hex.jsx +++ b/superset/assets/src/visualizations/deckgl/layers/Hex/Hex.jsx @@ -17,10 +17,22 @@ * under the License. */ import { HexagonLayer } from 'deck.gl'; +import React from 'react'; +import { t } from '@superset-ui/translation'; import { commonLayerProps, getAggFunc } from '../common'; import sandboxedEval from '../../../../modules/sandbox'; import { createDeckGLComponent } from '../../factory'; +import TooltipRow from '../../TooltipRow'; + +function setTooltipContent(o) { + return ( +
+ + +
+ ); +} export function getLayer(formData, payload, onAddFilter, setTooltip) { const fd = formData; @@ -47,7 +59,7 @@ export function getLayer(formData, payload, onAddFilter, setTooltip) { outline: false, getElevationValue: aggFunc, getColorValue: aggFunc, - ...commonLayerProps(fd, setTooltip), + ...commonLayerProps(fd, setTooltip, setTooltipContent), }); } diff --git a/superset/assets/src/visualizations/deckgl/layers/Path/Path.jsx b/superset/assets/src/visualizations/deckgl/layers/Path/Path.jsx index 633ccec9e..7bf098241 100644 --- a/superset/assets/src/visualizations/deckgl/layers/Path/Path.jsx +++ b/superset/assets/src/visualizations/deckgl/layers/Path/Path.jsx @@ -17,9 +17,24 @@ * under the License. */ import { PathLayer } from 'deck.gl'; +import React from 'react'; import { commonLayerProps } from '../common'; import sandboxedEval from '../../../../modules/sandbox'; import { createDeckGLComponent } from '../../factory'; +import TooltipRow from '../../TooltipRow'; + +function setTooltipContent(o) { + return ( + o.object.extraProps && +
+ { + Object.keys(o.object.extraProps).map((prop, index) => + , + ) + } +
+ ); +} export function getLayer(formData, payload, onAddFilter, setTooltip) { const fd = formData; @@ -42,7 +57,7 @@ export function getLayer(formData, payload, onAddFilter, setTooltip) { data, rounded: true, widthScale: 1, - ...commonLayerProps(fd, setTooltip), + ...commonLayerProps(fd, setTooltip, setTooltipContent), }); } diff --git a/superset/assets/src/visualizations/deckgl/layers/Polygon/Polygon.jsx b/superset/assets/src/visualizations/deckgl/layers/Polygon/Polygon.jsx index 8a3c2f402..ca7d4132a 100644 --- a/superset/assets/src/visualizations/deckgl/layers/Polygon/Polygon.jsx +++ b/superset/assets/src/visualizations/deckgl/layers/Polygon/Polygon.jsx @@ -25,6 +25,7 @@ import { PolygonLayer } from 'deck.gl'; import AnimatableDeckGLContainer from '../../AnimatableDeckGLContainer'; import Legend from '../../../Legend'; +import TooltipRow from '../../TooltipRow'; import { getBuckets, getBreakPointColorScaler } from '../../utils'; import { commonLayerProps, fitViewport } from '../common'; @@ -48,6 +49,18 @@ function getElevation(d, colorScaler) { : d.elevation; } +function setTooltipContent(formData) { + return (o) => { + const metricLabel = formData.metric.label || formData.metric; + return ( +
+ + {formData.metric && } +
+ ); + }; +} + export function getLayer(formData, payload, setTooltip, selected, onSelect, filters) { const fd = formData; const fc = fd.fill_color_picker; @@ -95,7 +108,7 @@ export function getLayer(formData, payload, setTooltip, selected, onSelect, filt getElevation: d => getElevation(d, colorScaler), elevationScale: fd.multiplier, fp64: true, - ...commonLayerProps(fd, setTooltip, onSelect), + ...commonLayerProps(fd, setTooltip, setTooltipContent(fd), onSelect), }); } diff --git a/superset/assets/src/visualizations/deckgl/layers/Scatter/Scatter.jsx b/superset/assets/src/visualizations/deckgl/layers/Scatter/Scatter.jsx index 194a7755a..8ca13aac4 100644 --- a/superset/assets/src/visualizations/deckgl/layers/Scatter/Scatter.jsx +++ b/superset/assets/src/visualizations/deckgl/layers/Scatter/Scatter.jsx @@ -17,14 +17,31 @@ * under the License. */ import { ScatterplotLayer } from 'deck.gl'; +import React from 'react'; +import { t } from '@superset-ui/translation'; import { commonLayerProps } from '../common'; import { createCategoricalDeckGLComponent } from '../../factory'; +import TooltipRow from '../../TooltipRow'; import { unitToRadius } from '../../../../modules/geo'; function getPoints(data) { return data.map(d => d.position); } +function setTooltipContent(formData) { + return o => ( +
+ + { + o.object.cat_color && + } + { + o.object.metric && + } +
+ ); +} + export function getLayer(fd, payload, onAddFilter, setTooltip) { const dataWithRadius = payload.data.features.map((d) => { let radius = unitToRadius(fd.point_unit, d.radius) || 10; @@ -46,7 +63,7 @@ export function getLayer(fd, payload, onAddFilter, setTooltip) { radiusMinPixels: fd.min_radius || null, radiusMaxPixels: fd.max_radius || null, outline: false, - ...commonLayerProps(fd, setTooltip), + ...commonLayerProps(fd, setTooltip, setTooltipContent(fd)), }); } diff --git a/superset/assets/src/visualizations/deckgl/layers/Screengrid/Screengrid.jsx b/superset/assets/src/visualizations/deckgl/layers/Screengrid/Screengrid.jsx index a4bdede05..1efefae21 100644 --- a/superset/assets/src/visualizations/deckgl/layers/Screengrid/Screengrid.jsx +++ b/superset/assets/src/visualizations/deckgl/layers/Screengrid/Screengrid.jsx @@ -21,15 +21,26 @@ import React from 'react'; import PropTypes from 'prop-types'; import { ScreenGridLayer } from 'deck.gl'; +import { t } from '@superset-ui/translation'; import AnimatableDeckGLContainer from '../../AnimatableDeckGLContainer'; import { getPlaySliderParams } from '../../../../modules/time'; import sandboxedEval from '../../../../modules/sandbox'; import { commonLayerProps, fitViewport } from '../common'; +import TooltipRow from '../../TooltipRow'; function getPoints(data) { return data.map(d => d.position); } +function setTooltipContent(o) { + return ( +
+ + +
+ ); +} + export function getLayer(formData, payload, onAddFilter, setTooltip, filters) { const fd = formData; const c = fd.color_picker; @@ -61,7 +72,7 @@ export function getLayer(formData, payload, onAddFilter, setTooltip, filters) { maxColor: [c.r, c.g, c.b, 255 * c.a], outline: false, getWeight: d => d.weight || 0, - ...commonLayerProps(fd, setTooltip), + ...commonLayerProps(fd, setTooltip, setTooltipContent), }); } diff --git a/superset/assets/src/visualizations/deckgl/layers/common.jsx b/superset/assets/src/visualizations/deckgl/layers/common.jsx index b9b271640..7adf3b789 100644 --- a/superset/assets/src/visualizations/deckgl/layers/common.jsx +++ b/superset/assets/src/visualizations/deckgl/layers/common.jsx @@ -16,8 +16,6 @@ * specific language governing permissions and limitations * under the License. */ -import React from 'react'; - import { fitBounds } from 'viewport-mercator-project'; import * as d3array from 'd3-array'; import sandboxedEval from '../../../modules/sandbox'; @@ -50,20 +48,12 @@ export function fitViewport(viewport, points, padding = 10) { } } -export function commonLayerProps(formData, setTooltip, onSelect) { +export function commonLayerProps(formData, setTooltip, setTooltipContent, onSelect) { const fd = formData; let onHover; - let tooltipContentGenerator; + let tooltipContentGenerator = setTooltipContent; if (fd.js_tooltip) { tooltipContentGenerator = sandboxedEval(fd.js_tooltip); - } else if (fd.line_column && fd.metric && ['geohash', 'zipcode'].indexOf(fd.line_type) >= 0) { - const metricLabel = fd.metric.label || fd.metric; - tooltipContentGenerator = o => ( -
-
{fd.line_column}: {o.object[fd.line_column]}
- {fd.metric && -
{metricLabel}: {o.object[metricLabel]}
} -
); } if (tooltipContentGenerator) { onHover = (o) => { diff --git a/superset/assets/src/visualizations/stylesheets/deckgl.css b/superset/assets/src/visualizations/stylesheets/deckgl.css new file mode 100644 index 000000000..b085d1c6b --- /dev/null +++ b/superset/assets/src/visualizations/stylesheets/deckgl.css @@ -0,0 +1,22 @@ +/** + * 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. + */ + .deckgl-tooltip > div { + overflow: hidden; + text-overflow: ellipsis; + } From 4ab89dbcf7a4cc1c142c2be6e6eb88fe1d009218 Mon Sep 17 00:00:00 2001 From: Beto Dealmeida Date: Wed, 3 Apr 2019 16:57:59 -0700 Subject: [PATCH 15/38] Celery task for warming up cache (#7148) * Sparkline dates aren't formatting in Time Series Table (#6976) * Exclude venv for python linter to ignore * Fix NaN error * Fix the white background shown in SQL editor on drag (#7021) This PR sets the background-color css property on `.ace_scroller` instead of `.ace_content` to prevent the white background shown during resizing of the SQL editor before drag ends. * Show tooltip with time frame (#6979) * Fix time filter control (#6978) * Enhancement of query context and object. (#6962) * added more functionalities for query context and object. * fixed cache logic * added default value for groupby * updated comments and removed print (cherry picked from commit d5b9795f87f79fa2c41e144ffc00fd9586be7657) * [fix] /superset/slice/id url is too long (#6989) (cherry picked from commit 6a4d507ab607b01ed324cb3341b71c6fb2cb5c97) * [WIP] fix user specified JSON metadata not updating dashboard on refresh (#7027) (cherry picked from commit cc58f0e661044e95c7c86d0da8d77a0a6640efe7) * feat: add ability to change font size in big number (#7003) * Add ability to change font sizes in Big Number * rename big number to header * Add comment to clarify font size values * Allow LIMIT to be specified in parameters (#7052) * [fix] Cursor jumping when editing chart and dashboard titles (#7038) (cherry picked from commit fc1770f7b79a4d8815b646b46390fabf190c3815) * Changing time table viz to pass formatTime a date (#7020) (cherry picked from commit 7f3c145b1f5a4e2d8b95982119503e98772e2c47) * [db-engine-spec] Aligning Hive/Presto partition logic (#7007) (cherry picked from commit 05be86611785fef2904992e4e7d31dce23f1c51b) * [fix] explore chart from dashboard missed slice title (#7046) (cherry picked from commit a6d48d4052839286aec725d51303b3b2bf6e8dd4) * fix inaccurate data calculation with adata rolling and contribution (#7035) (cherry picked from commit 0782e831cd37f665a2838119d87c433269f1b36b) * Adding warning message for sqllab save query (#7028) (cherry picked from commit ead3d48133e7e1ab8b91d51e561544a544b4eaad) * [datasource] Ensuring consistent behavior of datasource editing/saving. (#7037) * Update datasource.py * Update datasource.py (cherry picked from commit c771625f1068d3a7f41e6bced14b0cbdbf9962cc) * [csv-upload] Fixing message encoding (#6971) (cherry picked from commit 48431ab5b9375a94c5262a0336d9c69e5f01a3ac) * [sql-parse] Fixing LIMIT exceptions (#6963) (cherry picked from commit 3e076cb60b385e675ed1c9a8053493375e43370b) * Adding custom control overrides (#6956) * Adding extraOverrides to line chart * Updating extraOverrides to fit with more cases * Moving extraOverrides to index.js * Removing webpack-merge in package.json * Fixing metrics control clearing metric (cherry picked from commit e6194051f486e42922dc4e34a861f4490c1062fc) * [sqlparse] Fixing table name extraction for ill-defined query (#7029) (cherry picked from commit 07c340cf8203f13222f16efad1e55e202deb1865) * [missing values] Removing replacing missing values (#4905) (cherry picked from commit 61add606ca16a6ba981ccde864b121f5464b697a) * [SQL Lab] Improved query and results tabs rendering reliability (#7082) closes #7080 (cherry picked from commit 9b58e9f4920ef424e5b545dcbb4726e22bed5982) * Fix filter_box migration PR #6523 (#7066) * Fix filter_box migration PR #6523 * Fix druid-related bug (cherry picked from commit b210742ad24d01ca05bc58ca3342c90e301fe073) * SQL editor layout makeover (#7102) This PR includes the following layout and css tweaks: - Using flex to layout the north and south sub panes of query pane so resizing works properly in both Chrome and Firefox - Removal of necessary wrapper divs and tweaking of css in sql lab so we can scroll to the bottom of both the table list and the results pane - Make sql lab's content not overflow vertically and layout the query result area to eliminate double scroll bars - css tweaks on the basic.html page so the loading animation appears in the center of the page across the board (cherry picked from commit 71f1bbd2ec59b99d6ba6d9a4a2f9cfceaf922b80) * [forms] Fix handling of NULLs (cherry picked from commit e83a07d3dfda350cc44041cb6cbaec4510887902) * handle null column_name in sqla and druid models (cherry picked from commit 2ff721ae072b8d69c5cabddc3e1a388a596b1b6f) * Use metric name instead of metric in filter box (#7106) (cherry picked from commit 003364e74ea70cad1a4a6e784933fe8bef4c78ec) * Bump python lib croniter to an existing version (#7132) Package maintainers should really never delete packages, but it appears this happened with croniter and resulted in breaking our builds. This PR bumps to a more recent existing version of the library (cherry picked from commit 215ed392a11598eac228f57341dbfd232cf770e3) * Revert PR #6933 (#7162) * Celery worker for warming up cache * Remove testing changes * Add documentation * Fix lint * WIP dashboard filters * Use new cache so it works with dashboards * Add more unit tests, fix old ones * Fix flake8 and docs * Sparkline dates aren't formatting in Time Series Table (#6976) * Exclude venv for python linter to ignore * Fix NaN error * Changing time table viz to pass formatTime a date (#7020) (cherry picked from commit 7f3c145b1f5a4e2d8b95982119503e98772e2c47) * SQL editor layout makeover (#7102) This PR includes the following layout and css tweaks: - Using flex to layout the north and south sub panes of query pane so resizing works properly in both Chrome and Firefox - Removal of necessary wrapper divs and tweaking of css in sql lab so we can scroll to the bottom of both the table list and the results pane - Make sql lab's content not overflow vertically and layout the query result area to eliminate double scroll bars - css tweaks on the basic.html page so the loading animation appears in the center of the page across the board (cherry picked from commit 71f1bbd2ec59b99d6ba6d9a4a2f9cfceaf922b80) * Celery worker for warming up cache * Remove testing changes * Add documentation * Fix lint * WIP dashboard filters * Use new cache so it works with dashboards * Add more unit tests, fix old ones * Fix flake8 and docs * Fix bad merge and pylint --- docs/installation.rst | 20 ++ superset/assets/package-lock.json | 41 ++-- superset/tasks/__init__.py | 1 + superset/tasks/cache.py | 316 ++++++++++++++++++++++++++++++ tests/strategy_tests.py | 236 ++++++++++++++++++++++ 5 files changed, 584 insertions(+), 30 deletions(-) create mode 100644 superset/tasks/cache.py create mode 100644 tests/strategy_tests.py diff --git a/docs/installation.rst b/docs/installation.rst index 7bc9f61f3..a06da876d 100644 --- a/docs/installation.rst +++ b/docs/installation.rst @@ -479,6 +479,26 @@ into your global default defined in ``CACHE_CONFIG``. 'CACHE_REDIS_URL': 'redis://localhost:6379/0', } +Superset has a Celery task that will periodically warm up the cache based on +different strategies. To use it, add the following to the `CELERYBEAT_SCHEDULE` +section in `config.py`: + +.. code-block:: python + + CELERYBEAT_SCHEDULE = { + 'cache-warmup-hourly': { + 'task': 'cache-warmup', + 'schedule': crontab(minute=0, hour='*'), # hourly + 'kwargs': { + 'strategy_name': 'top_n_dashboards', + 'top_n': 5, + 'since': '7 days ago', + }, + }, + } + +This will cache all the charts in the top 5 most popular dashboards every hour. +For other strategies, check the `superset/tasks/cache.py` file. Deeper SQLAlchemy integration diff --git a/superset/assets/package-lock.json b/superset/assets/package-lock.json index 8eeca9bde..110e53ac3 100644 --- a/superset/assets/package-lock.json +++ b/superset/assets/package-lock.json @@ -8684,8 +8684,7 @@ "ansi-regex": { "version": "2.1.1", "bundled": true, - "dev": true, - "optional": true + "dev": true }, "aproba": { "version": "1.2.0", @@ -8708,14 +8707,12 @@ "balanced-match": { "version": "1.0.0", "bundled": true, - "dev": true, - "optional": true + "dev": true }, "brace-expansion": { "version": "1.1.11", "bundled": true, "dev": true, - "optional": true, "requires": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" @@ -8731,20 +8728,17 @@ "code-point-at": { "version": "1.1.0", "bundled": true, - "dev": true, - "optional": true + "dev": true }, "concat-map": { "version": "0.0.1", "bundled": true, - "dev": true, - "optional": true + "dev": true }, "console-control-strings": { "version": "1.1.0", "bundled": true, - "dev": true, - "optional": true + "dev": true }, "core-util-is": { "version": "1.0.2", @@ -8874,8 +8868,7 @@ "inherits": { "version": "2.0.3", "bundled": true, - "dev": true, - "optional": true + "dev": true }, "ini": { "version": "1.3.5", @@ -8888,7 +8881,6 @@ "version": "1.0.0", "bundled": true, "dev": true, - "optional": true, "requires": { "number-is-nan": "^1.0.0" } @@ -8904,7 +8896,6 @@ "version": "3.0.4", "bundled": true, "dev": true, - "optional": true, "requires": { "brace-expansion": "^1.1.7" } @@ -8912,14 +8903,12 @@ "minimist": { "version": "0.0.8", "bundled": true, - "dev": true, - "optional": true + "dev": true }, "minipass": { "version": "2.2.4", "bundled": true, "dev": true, - "optional": true, "requires": { "safe-buffer": "^5.1.1", "yallist": "^3.0.0" @@ -8939,7 +8928,6 @@ "version": "0.5.1", "bundled": true, "dev": true, - "optional": true, "requires": { "minimist": "0.0.8" } @@ -9027,8 +9015,7 @@ "number-is-nan": { "version": "1.0.1", "bundled": true, - "dev": true, - "optional": true + "dev": true }, "object-assign": { "version": "4.1.1", @@ -9041,7 +9028,6 @@ "version": "1.4.0", "bundled": true, "dev": true, - "optional": true, "requires": { "wrappy": "1" } @@ -9136,8 +9122,7 @@ "safe-buffer": { "version": "5.1.1", "bundled": true, - "dev": true, - "optional": true + "dev": true }, "safer-buffer": { "version": "2.1.2", @@ -9178,7 +9163,6 @@ "version": "1.0.2", "bundled": true, "dev": true, - "optional": true, "requires": { "code-point-at": "^1.0.0", "is-fullwidth-code-point": "^1.0.0", @@ -9199,7 +9183,6 @@ "version": "3.0.1", "bundled": true, "dev": true, - "optional": true, "requires": { "ansi-regex": "^2.0.0" } @@ -9247,14 +9230,12 @@ "wrappy": { "version": "1.0.2", "bundled": true, - "dev": true, - "optional": true + "dev": true }, "yallist": { "version": "3.0.2", "bundled": true, - "dev": true, - "optional": true + "dev": true } } }, diff --git a/superset/tasks/__init__.py b/superset/tasks/__init__.py index 58702ac4c..c2be5085f 100644 --- a/superset/tasks/__init__.py +++ b/superset/tasks/__init__.py @@ -16,3 +16,4 @@ # specific language governing permissions and limitations # under the License. from . import schedules # noqa +from . import cache # noqa diff --git a/superset/tasks/cache.py b/superset/tasks/cache.py new file mode 100644 index 000000000..831bb6642 --- /dev/null +++ b/superset/tasks/cache.py @@ -0,0 +1,316 @@ +# 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. +# pylint: disable=too-few-public-methods + +import json +import logging +import urllib.parse + +from celery.utils.log import get_task_logger +from flask import url_for +import requests +from requests.exceptions import RequestException +from sqlalchemy import and_, func + +from superset import app, db +from superset.models.core import Dashboard, Log, Slice +from superset.models.tags import Tag, TaggedObject +from superset.tasks.celery_app import app as celery_app +from superset.utils.core import parse_human_datetime + + +logger = get_task_logger(__name__) +logger.setLevel(logging.INFO) + + +def get_form_data(chart_id, dashboard=None): + """ + Build `form_data` for chart GET request from dashboard's `default_filters`. + + When a dashboard has `default_filters` they need to be added as extra + filters in the GET request for charts. + + """ + form_data = {'slice_id': chart_id} + + if dashboard is None or not dashboard.json_metadata: + return form_data + + json_metadata = json.loads(dashboard.json_metadata) + + # do not apply filters if chart is immune to them + if chart_id in json_metadata.get('filter_immune_slices', []): + return form_data + + default_filters = json.loads(json_metadata.get('default_filters', 'null')) + if not default_filters: + return form_data + + # are some of the fields in the chart immune to filters? + filter_immune_slice_fields = json_metadata.get('filter_immune_slice_fields', {}) + immune_fields = filter_immune_slice_fields.get(str(chart_id), []) + + extra_filters = [] + for filters in default_filters.values(): + for col, val in filters.items(): + if col not in immune_fields: + extra_filters.append({'col': col, 'op': 'in', 'val': val}) + if extra_filters: + form_data['extra_filters'] = extra_filters + + return form_data + + +def get_url(params): + """Return external URL for warming up a given chart/table cache.""" + baseurl = 'http://{SUPERSET_WEBSERVER_ADDRESS}:{SUPERSET_WEBSERVER_PORT}/'.format( + **app.config) + with app.test_request_context(): + return urllib.parse.urljoin( + baseurl, + url_for('Superset.explore_json', **params), + ) + + +class Strategy: + """ + A cache warm up strategy. + + Each strategy defines a `get_urls` method that returns a list of URLs to + be fetched from the `/superset/warm_up_cache/` endpoint. + + Strategies can be configured in `superset/config.py`: + + CELERYBEAT_SCHEDULE = { + 'cache-warmup-hourly': { + 'task': 'cache-warmup', + 'schedule': crontab(minute=1, hour='*'), # @hourly + 'kwargs': { + 'strategy_name': 'top_n_dashboards', + 'top_n': 10, + 'since': '7 days ago', + }, + }, + } + + """ + def __init__(self): + pass + + def get_urls(self): + raise NotImplementedError('Subclasses must implement get_urls!') + + +class DummyStrategy(Strategy): + """ + Warm up all charts. + + This is a dummy strategy that will fetch all charts. Can be configured by: + + CELERYBEAT_SCHEDULE = { + 'cache-warmup-hourly': { + 'task': 'cache-warmup', + 'schedule': crontab(minute=1, hour='*'), # @hourly + 'kwargs': {'strategy_name': 'dummy'}, + }, + } + + """ + + name = 'dummy' + + def get_urls(self): + session = db.create_scoped_session() + charts = session.query(Slice).all() + + return [get_url({'form_data': get_form_data(chart.id)}) for chart in charts] + + +class TopNDashboardsStrategy(Strategy): + """ + Warm up charts in the top-n dashboards. + + CELERYBEAT_SCHEDULE = { + 'cache-warmup-hourly': { + 'task': 'cache-warmup', + 'schedule': crontab(minute=1, hour='*'), # @hourly + 'kwargs': { + 'strategy_name': 'top_n_dashboards', + 'top_n': 5, + 'since': '7 days ago', + }, + }, + } + + """ + + name = 'top_n_dashboards' + + def __init__(self, top_n=5, since='7 days ago'): + super(TopNDashboardsStrategy, self).__init__() + self.top_n = top_n + self.since = parse_human_datetime(since) + + def get_urls(self): + urls = [] + session = db.create_scoped_session() + + records = ( + session + .query(Log.dashboard_id, func.count(Log.dashboard_id)) + .filter(and_( + Log.dashboard_id.isnot(None), + Log.dttm >= self.since, + )) + .group_by(Log.dashboard_id) + .order_by(func.count(Log.dashboard_id).desc()) + .limit(self.top_n) + .all() + ) + dash_ids = [record.dashboard_id for record in records] + dashboards = ( + session + .query(Dashboard) + .filter(Dashboard.id.in_(dash_ids)) + .all() + ) + for dashboard in dashboards: + for chart in dashboard.slices: + urls.append( + get_url({'form_data': get_form_data(chart.id, dashboard)})) + + return urls + + +class DashboardTagsStrategy(Strategy): + """ + Warm up charts in dashboards with custom tags. + + CELERYBEAT_SCHEDULE = { + 'cache-warmup-hourly': { + 'task': 'cache-warmup', + 'schedule': crontab(minute=1, hour='*'), # @hourly + 'kwargs': { + 'strategy_name': 'dashboard_tags', + 'tags': ['core', 'warmup'], + }, + }, + } + """ + + name = 'dashboard_tags' + + def __init__(self, tags=None): + super(DashboardTagsStrategy, self).__init__() + self.tags = tags or [] + + def get_urls(self): + urls = [] + session = db.create_scoped_session() + + tags = ( + session + .query(Tag) + .filter(Tag.name.in_(self.tags)) + .all() + ) + tag_ids = [tag.id for tag in tags] + + # add dashboards that are tagged + tagged_objects = ( + session + .query(TaggedObject) + .filter(and_( + TaggedObject.object_type == 'dashboard', + TaggedObject.tag_id.in_(tag_ids), + )) + .all() + ) + dash_ids = [tagged_object.object_id for tagged_object in tagged_objects] + tagged_dashboards = ( + session + .query(Dashboard) + .filter(Dashboard.id.in_(dash_ids)) + ) + for dashboard in tagged_dashboards: + for chart in dashboard.slices: + urls.append( + get_url({'form_data': get_form_data(chart.id, dashboard)})) + + # add charts that are tagged + tagged_objects = ( + session + .query(TaggedObject) + .filter(and_( + TaggedObject.object_type == 'chart', + TaggedObject.tag_id.in_(tag_ids), + )) + .all() + ) + chart_ids = [tagged_object.object_id for tagged_object in tagged_objects] + tagged_charts = ( + session + .query(Slice) + .filter(Slice.id.in_(chart_ids)) + ) + for chart in tagged_charts: + urls.append(get_url({'form_data': get_form_data(chart.id)})) + + return urls + + +strategies = [DummyStrategy, TopNDashboardsStrategy, DashboardTagsStrategy] + + +@celery_app.task(name='cache-warmup') +def cache_warmup(strategy_name, *args, **kwargs): + """ + Warm up cache. + + This task periodically hits charts to warm up the cache. + + """ + logger.info('Loading strategy') + class_ = None + for class_ in strategies: + if class_.name == strategy_name: + break + else: + message = f'No strategy {strategy_name} found!' + logger.error(message) + return message + + logger.info(f'Loading {class_.__name__}') + try: + strategy = class_(*args, **kwargs) + logger.info('Success!') + except TypeError: + message = 'Error loading strategy!' + logger.exception(message) + return message + + results = {'success': [], 'errors': []} + for url in strategy.get_urls(): + try: + logger.info(f'Fetching {url}') + requests.get(url) + results['success'].append(url) + except RequestException: + logger.exception('Error warming up cache!') + results['errors'].append(url) + + return results diff --git a/tests/strategy_tests.py b/tests/strategy_tests.py new file mode 100644 index 000000000..204f9547c --- /dev/null +++ b/tests/strategy_tests.py @@ -0,0 +1,236 @@ +# 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. +"""Unit tests for Superset cache warmup""" +import json +from unittest.mock import MagicMock + +from superset import db +from superset.models.core import Log +from superset.models.tags import ( + get_tag, + ObjectTypes, + TaggedObject, + TagTypes, +) +from superset.tasks.cache import ( + DashboardTagsStrategy, + DummyStrategy, + get_form_data, + TopNDashboardsStrategy, +) +from .base_tests import SupersetTestCase + + +TEST_URL = 'http://0.0.0.0:8081/superset/explore_json' + + +class CacheWarmUpTests(SupersetTestCase): + + def __init__(self, *args, **kwargs): + super(CacheWarmUpTests, self).__init__(*args, **kwargs) + + def test_get_form_data_chart_only(self): + chart_id = 1 + result = get_form_data(chart_id, None) + expected = {'slice_id': chart_id} + self.assertEqual(result, expected) + + def test_get_form_data_no_dashboard_metadata(self): + chart_id = 1 + dashboard = MagicMock() + dashboard.json_metadata = None + result = get_form_data(chart_id, dashboard) + expected = {'slice_id': chart_id} + self.assertEqual(result, expected) + + def test_get_form_data_immune_slice(self): + chart_id = 1 + filter_box_id = 2 + dashboard = MagicMock() + dashboard.json_metadata = json.dumps({ + 'filter_immune_slices': [chart_id], + 'default_filters': json.dumps({ + str(filter_box_id): {'name': ['Alice', 'Bob']}, + }), + }) + result = get_form_data(chart_id, dashboard) + expected = {'slice_id': chart_id} + self.assertEqual(result, expected) + + def test_get_form_data_no_default_filters(self): + chart_id = 1 + dashboard = MagicMock() + dashboard.json_metadata = json.dumps({}) + result = get_form_data(chart_id, dashboard) + expected = {'slice_id': chart_id} + self.assertEqual(result, expected) + + def test_get_form_data_immune_fields(self): + chart_id = 1 + filter_box_id = 2 + dashboard = MagicMock() + dashboard.json_metadata = json.dumps({ + 'default_filters': json.dumps({ + str(filter_box_id): { + 'name': ['Alice', 'Bob'], + '__time_range': '100 years ago : today', + }, + }), + 'filter_immune_slice_fields': {chart_id: ['__time_range']}, + }) + result = get_form_data(chart_id, dashboard) + expected = { + 'slice_id': chart_id, + 'extra_filters': [ + { + 'col': 'name', + 'op': 'in', + 'val': ['Alice', 'Bob'], + }, + ], + } + self.assertEqual(result, expected) + + def test_get_form_data_no_extra_filters(self): + chart_id = 1 + filter_box_id = 2 + dashboard = MagicMock() + dashboard.json_metadata = json.dumps({ + 'default_filters': json.dumps({ + str(filter_box_id): { + '__time_range': '100 years ago : today', + }, + }), + 'filter_immune_slice_fields': {chart_id: ['__time_range']}, + }) + result = get_form_data(chart_id, dashboard) + expected = {'slice_id': chart_id} + self.assertEqual(result, expected) + + def test_get_form_data(self): + chart_id = 1 + filter_box_id = 2 + dashboard = MagicMock() + dashboard.json_metadata = json.dumps({ + 'default_filters': json.dumps({ + str(filter_box_id): { + 'name': ['Alice', 'Bob'], + '__time_range': '100 years ago : today', + }, + }), + }) + result = get_form_data(chart_id, dashboard) + expected = { + 'slice_id': chart_id, + 'extra_filters': [ + { + 'col': 'name', + 'op': 'in', + 'val': ['Alice', 'Bob'], + }, + { + 'col': '__time_range', + 'op': 'in', + 'val': '100 years ago : today', + }, + ], + } + self.assertEqual(result, expected) + + def test_dummy_strategy(self): + strategy = DummyStrategy() + result = sorted(strategy.get_urls()) + expected = [ + f'{TEST_URL}/?form_data=%7B%27slice_id%27%3A+1%7D', + f'{TEST_URL}/?form_data=%7B%27slice_id%27%3A+17%7D', + f'{TEST_URL}/?form_data=%7B%27slice_id%27%3A+18%7D', + f'{TEST_URL}/?form_data=%7B%27slice_id%27%3A+19%7D', + f'{TEST_URL}/?form_data=%7B%27slice_id%27%3A+30%7D', + f'{TEST_URL}/?form_data=%7B%27slice_id%27%3A+31%7D', + f'{TEST_URL}/?form_data=%7B%27slice_id%27%3A+8%7D', + ] + self.assertEqual(result, expected) + + def test_top_n_dashboards_strategy(self): + # create a top visited dashboard + db.session.query(Log).delete() + self.login(username='admin') + for _ in range(10): + self.client.get('/superset/dashboard/3/') + + strategy = TopNDashboardsStrategy(1) + result = sorted(strategy.get_urls()) + expected = [ + f'{TEST_URL}/?form_data=%7B%27slice_id%27%3A+31%7D', + ] + self.assertEqual(result, expected) + + def test_dashboard_tags(self): + strategy = DashboardTagsStrategy(['tag1']) + + result = sorted(strategy.get_urls()) + expected = [] + self.assertEqual(result, expected) + + # tag dashboard 3 with `tag1` + tag1 = get_tag('tag1', db.session, TagTypes.custom) + object_id = 3 + tagged_object = TaggedObject( + tag_id=tag1.id, + object_id=object_id, + object_type=ObjectTypes.dashboard, + ) + db.session.add(tagged_object) + db.session.commit() + + result = sorted(strategy.get_urls()) + expected = [ + f'{TEST_URL}/?form_data=%7B%27slice_id%27%3A+31%7D', + ] + self.assertEqual(result, expected) + + strategy = DashboardTagsStrategy(['tag2']) + + result = sorted(strategy.get_urls()) + expected = [] + self.assertEqual(result, expected) + + # tag chart 30 with `tag2` + tag2 = get_tag('tag2', db.session, TagTypes.custom) + object_id = 30 + tagged_object = TaggedObject( + tag_id=tag2.id, + object_id=object_id, + object_type=ObjectTypes.chart, + ) + db.session.add(tagged_object) + db.session.commit() + + result = sorted(strategy.get_urls()) + expected = [ + f'{TEST_URL}/?form_data=%7B%27slice_id%27%3A+30%7D', + ] + self.assertEqual(result, expected) + + strategy = DashboardTagsStrategy(['tag1', 'tag2']) + + result = sorted(strategy.get_urls()) + expected = [ + f'{TEST_URL}/?form_data=%7B%27slice_id%27%3A+30%7D', + f'{TEST_URL}/?form_data=%7B%27slice_id%27%3A+31%7D', + ] + self.assertEqual(result, expected) From a875239259052aafe3cbcb2ad65530b86a5fb2e6 Mon Sep 17 00:00:00 2001 From: Kim Truong <47833996+khtruong@users.noreply.github.com> Date: Wed, 3 Apr 2019 18:44:03 -0700 Subject: [PATCH 16/38] fix: add check for polygon tooltips (#7226) --- .../src/visualizations/deckgl/layers/Polygon/Polygon.jsx | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/superset/assets/src/visualizations/deckgl/layers/Polygon/Polygon.jsx b/superset/assets/src/visualizations/deckgl/layers/Polygon/Polygon.jsx index ca7d4132a..7e30668e2 100644 --- a/superset/assets/src/visualizations/deckgl/layers/Polygon/Polygon.jsx +++ b/superset/assets/src/visualizations/deckgl/layers/Polygon/Polygon.jsx @@ -94,6 +94,9 @@ export function getLayer(formData, payload, setTooltip, selected, onSelect, filt } return baseColor; }; + const tooltipContentGenerator = (fd.line_column && fd.metric && ['geohash', 'zipcode'].indexOf(fd.line_type) >= 0) + ? setTooltipContent(fd) + : undefined; return new PolygonLayer({ id: `path-layer-${fd.slice_id}`, data, @@ -108,7 +111,7 @@ export function getLayer(formData, payload, setTooltip, selected, onSelect, filt getElevation: d => getElevation(d, colorScaler), elevationScale: fd.multiplier, fp64: true, - ...commonLayerProps(fd, setTooltip, setTooltipContent(fd), onSelect), + ...commonLayerProps(fd, setTooltip, tooltipContentGenerator, onSelect), }); } From 43874b3750497ee9c2c16b3c8e7f298cf63c5564 Mon Sep 17 00:00:00 2001 From: Beto Dealmeida Date: Wed, 3 Apr 2019 21:16:23 -0700 Subject: [PATCH 17/38] Fix time grain after revert (#7228) --- superset/assets/src/explore/controls.jsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/superset/assets/src/explore/controls.jsx b/superset/assets/src/explore/controls.jsx index c72348a09..eb1f67036 100644 --- a/superset/assets/src/explore/controls.jsx +++ b/superset/assets/src/explore/controls.jsx @@ -916,7 +916,7 @@ export const controls = { 'The options here are defined on a per database ' + 'engine basis in the Superset source code.'), mapStateToProps: state => ({ - choices: (state.datasource) ? state.datasource.timeGrainSqla : null, + choices: (state.datasource) ? state.datasource.time_grain_sqla : null, }), }, From b4915caa6e1751b80bf6ec9e9d80959d0aa86850 Mon Sep 17 00:00:00 2001 From: Kim Truong <47833996+khtruong@users.noreply.github.com> Date: Thu, 4 Apr 2019 12:57:57 -0700 Subject: [PATCH 18/38] fix: give a more accurate time (#7221) --- superset/assets/src/chart/chartReducer.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/superset/assets/src/chart/chartReducer.js b/superset/assets/src/chart/chartReducer.js index f626be701..e7ad6c6a5 100644 --- a/superset/assets/src/chart/chartReducer.js +++ b/superset/assets/src/chart/chartReducer.js @@ -47,7 +47,6 @@ export default function chartReducer(charts = {}, action) { return { ...state, chartStatus: 'success', queryResponse: action.queryResponse, - chartUpdateEndTime: now(), chartAlert: null, }; }, @@ -72,6 +71,7 @@ export default function chartReducer(charts = {}, action) { [actions.CHART_RENDERING_SUCCEEDED](state) { return { ...state, chartStatus: 'rendered', + chartUpdateEndTime: now(), }; }, [actions.CHART_RENDERING_FAILED](state) { From e39b16994946b2a7975af3ed76245e77303329e2 Mon Sep 17 00:00:00 2001 From: Maxime Beauchemin Date: Thu, 4 Apr 2019 22:17:35 -0700 Subject: [PATCH 19/38] [filter_box] allow empty filters list (#7220) in some cases, people want a time filter only on filter box, without specifying dimensions (filters), this allows that --- superset/assets/src/explore/controls.jsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/superset/assets/src/explore/controls.jsx b/superset/assets/src/explore/controls.jsx index eb1f67036..be1e56f04 100644 --- a/superset/assets/src/explore/controls.jsx +++ b/superset/assets/src/explore/controls.jsx @@ -2382,7 +2382,7 @@ export const controls = { type: 'CollectionControl', label: 'Filters', description: t('Filter configuration for the filter box'), - validators: [v.nonEmpty], + validators: [], controlName: 'FilterBoxItemControl', mapStateToProps: ({ datasource }) => ({ datasource }), }, From 74bdc3248a3834b26c68aa4d97e2aef0ac612e9f Mon Sep 17 00:00:00 2001 From: Thomas Wang <17309187+datability-io@users.noreply.github.com> Date: Thu, 4 Apr 2019 23:05:14 -0700 Subject: [PATCH 20/38] increment version number for nvd3 (#7232) * increment version number for nvd3 * fix: update package-lock.json * fix: update package-lock.json * remove package-lock.json created by accident --- superset/assets/package-lock.json | 47 ++++++++++++++++++++++--------- superset/assets/package.json | 2 +- 2 files changed, 34 insertions(+), 15 deletions(-) diff --git a/superset/assets/package-lock.json b/superset/assets/package-lock.json index 110e53ac3..cf656671b 100644 --- a/superset/assets/package-lock.json +++ b/superset/assets/package-lock.json @@ -2594,9 +2594,9 @@ } }, "@superset-ui/legacy-preset-chart-nvd3": { - "version": "0.10.4", - "resolved": "https://registry.npmjs.org/@superset-ui/legacy-preset-chart-nvd3/-/legacy-preset-chart-nvd3-0.10.4.tgz", - "integrity": "sha512-sQhsBookQevxuIKHHSPojZ4QqL02XWKfuhhpwhnLt487xJP94bYPJE2P/fZ/dZILkEfIjswmBML3GJ5XlvmJZw==", + "version": "0.10.7", + "resolved": "https://registry.npmjs.org/@superset-ui/legacy-preset-chart-nvd3/-/legacy-preset-chart-nvd3-0.10.7.tgz", + "integrity": "sha512-Zhkyz3NNtKHeQ8SwU64aKCJmaOuf63/5wm2/Iu+nV8U/8KcnqFpQklBwjOlURt7tiKe7Jx+HGT3k0RMX/16rYg==", "requires": { "@data-ui/xy-chart": "^0.0.75", "d3": "^3.5.17", @@ -8684,7 +8684,8 @@ "ansi-regex": { "version": "2.1.1", "bundled": true, - "dev": true + "dev": true, + "optional": true }, "aproba": { "version": "1.2.0", @@ -8707,12 +8708,14 @@ "balanced-match": { "version": "1.0.0", "bundled": true, - "dev": true + "dev": true, + "optional": true }, "brace-expansion": { "version": "1.1.11", "bundled": true, "dev": true, + "optional": true, "requires": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" @@ -8728,17 +8731,20 @@ "code-point-at": { "version": "1.1.0", "bundled": true, - "dev": true + "dev": true, + "optional": true }, "concat-map": { "version": "0.0.1", "bundled": true, - "dev": true + "dev": true, + "optional": true }, "console-control-strings": { "version": "1.1.0", "bundled": true, - "dev": true + "dev": true, + "optional": true }, "core-util-is": { "version": "1.0.2", @@ -8868,7 +8874,8 @@ "inherits": { "version": "2.0.3", "bundled": true, - "dev": true + "dev": true, + "optional": true }, "ini": { "version": "1.3.5", @@ -8881,6 +8888,7 @@ "version": "1.0.0", "bundled": true, "dev": true, + "optional": true, "requires": { "number-is-nan": "^1.0.0" } @@ -8896,6 +8904,7 @@ "version": "3.0.4", "bundled": true, "dev": true, + "optional": true, "requires": { "brace-expansion": "^1.1.7" } @@ -8903,12 +8912,14 @@ "minimist": { "version": "0.0.8", "bundled": true, - "dev": true + "dev": true, + "optional": true }, "minipass": { "version": "2.2.4", "bundled": true, "dev": true, + "optional": true, "requires": { "safe-buffer": "^5.1.1", "yallist": "^3.0.0" @@ -8928,6 +8939,7 @@ "version": "0.5.1", "bundled": true, "dev": true, + "optional": true, "requires": { "minimist": "0.0.8" } @@ -9015,7 +9027,8 @@ "number-is-nan": { "version": "1.0.1", "bundled": true, - "dev": true + "dev": true, + "optional": true }, "object-assign": { "version": "4.1.1", @@ -9028,6 +9041,7 @@ "version": "1.4.0", "bundled": true, "dev": true, + "optional": true, "requires": { "wrappy": "1" } @@ -9122,7 +9136,8 @@ "safe-buffer": { "version": "5.1.1", "bundled": true, - "dev": true + "dev": true, + "optional": true }, "safer-buffer": { "version": "2.1.2", @@ -9163,6 +9178,7 @@ "version": "1.0.2", "bundled": true, "dev": true, + "optional": true, "requires": { "code-point-at": "^1.0.0", "is-fullwidth-code-point": "^1.0.0", @@ -9183,6 +9199,7 @@ "version": "3.0.1", "bundled": true, "dev": true, + "optional": true, "requires": { "ansi-regex": "^2.0.0" } @@ -9230,12 +9247,14 @@ "wrappy": { "version": "1.0.2", "bundled": true, - "dev": true + "dev": true, + "optional": true }, "yallist": { "version": "3.0.2", "bundled": true, - "dev": true + "dev": true, + "optional": true } } }, diff --git a/superset/assets/package.json b/superset/assets/package.json index 84c15aeb4..283bee87d 100644 --- a/superset/assets/package.json +++ b/superset/assets/package.json @@ -76,7 +76,7 @@ "@superset-ui/legacy-plugin-chart-word-cloud": "^0.10.0", "@superset-ui/legacy-plugin-chart-world-map": "^0.10.0", "@superset-ui/legacy-preset-chart-big-number": "^0.10.4", - "@superset-ui/legacy-preset-chart-nvd3": "^0.10.4", + "@superset-ui/legacy-preset-chart-nvd3": "^0.10.6", "@superset-ui/number-format": "^0.10.1", "@superset-ui/time-format": "^0.10.1", "@superset-ui/translation": "^0.10.0", From c6876f7829ee0adc3430ae5305ec05005402cb0d Mon Sep 17 00:00:00 2001 From: Beto Dealmeida Date: Mon, 8 Apr 2019 11:06:45 -0700 Subject: [PATCH 21/38] Fix for tagging backend (#7240) --- superset/models/tags.py | 4 +- superset/views/tags.py | 172 ++++++++++++++++++++++------------------ 2 files changed, 96 insertions(+), 80 deletions(-) diff --git a/superset/models/tags.py b/superset/models/tags.py index 897c18928..9c30f55e5 100644 --- a/superset/models/tags.py +++ b/superset/models/tags.py @@ -123,7 +123,7 @@ class ObjectUpdater(object): tagged_object = TaggedObject( tag_id=tag.id, object_id=target.id, - object_type=ObjectTypes.chart, + object_type=cls.object_type, ) session.add(tagged_object) @@ -141,7 +141,7 @@ class ObjectUpdater(object): tagged_object = TaggedObject( tag_id=tag.id, object_id=target.id, - object_type=ObjectTypes.query, + object_type=cls.object_type, ) session.add(tagged_object) diff --git a/superset/views/tags.py b/superset/views/tags.py index fc34490c0..65cbbc966 100644 --- a/superset/views/tags.py +++ b/superset/views/tags.py @@ -30,7 +30,7 @@ from werkzeug.routing import BaseConverter from superset import app, appbuilder, db, utils from superset.jinja_context import current_user_id, current_username -import superset.models.core +from superset.models.core import Dashboard, Slice from superset.models.sql_lab import SavedQuery from superset.models.tags import ObjectTypes, Tag, TaggedObject, TagTypes from .base import BaseSupersetView, json_success @@ -57,33 +57,6 @@ def process_template(content): return template.render(context) -def get_name(obj): - if obj.Dashboard: - return obj.Dashboard.dashboard_title - elif obj.Slice: - return obj.Slice.slice_name - elif obj.SavedQuery: - return obj.SavedQuery.label - - -def get_creator(obj): - if obj.Dashboard: - return obj.Dashboard.creator() - elif obj.Slice: - return obj.Slice.creator() - elif obj.SavedQuery: - return obj.SavedQuery.creator() - - -def get_attribute(obj, attr): - if obj.Dashboard: - return getattr(obj.Dashboard, attr) - elif obj.Slice: - return getattr(obj.Slice, attr) - elif obj.SavedQuery: - return getattr(obj.SavedQuery, attr) - - class TagView(BaseSupersetView): @has_access_api @@ -91,11 +64,13 @@ class TagView(BaseSupersetView): def suggestions(self): query = ( db.session.query(TaggedObject) - .group_by(TaggedObject.tag_id) + .join(Tag) + .with_entities(TaggedObject.tag_id, Tag.name) + .group_by(TaggedObject.tag_id, Tag.name) .order_by(func.count().desc()) .all() ) - tags = [{'id': obj.tag.id, 'name': obj.tag.name} for obj in query] + tags = [{'id': id, 'name': name} for id, name in query] return json_success(json.dumps(tags)) @has_access_api @@ -157,60 +132,101 @@ class TagView(BaseSupersetView): @has_access_api @expose('/tagged_objects/', methods=['GET', 'POST']) def tagged_objects(self): - query = db.session.query( - TaggedObject, - superset.models.core.Dashboard, - superset.models.core.Slice, - SavedQuery, - ).join(Tag) - - tags = request.args.get('tags') + tags = [ + process_template(tag) + for tag in request.args.get('tags', '').split(',') if tag + ] if not tags: return json_success(json.dumps([])) - tags = [process_template(tag) for tag in tags.split(',')] - query = query.filter(Tag.name.in_(tags)) - # filter types - types = request.args.get('types') - if types: - query = query.filter(TaggedObject.object_type.in_(types.split(','))) - - # get names - query = query.outerjoin( - superset.models.core.Dashboard, - and_( - TaggedObject.object_id == superset.models.core.Dashboard.id, - TaggedObject.object_type == ObjectTypes.dashboard, - ), - ).outerjoin( - superset.models.core.Slice, - and_( - TaggedObject.object_id == superset.models.core.Slice.id, - TaggedObject.object_type == ObjectTypes.chart, - ), - ).outerjoin( - SavedQuery, - and_( - TaggedObject.object_id == SavedQuery.id, - TaggedObject.object_type == ObjectTypes.query, - ), - ).group_by(TaggedObject.object_id, TaggedObject.object_type) - - objects = [ - { - 'id': get_attribute(obj, 'id'), - 'type': obj.TaggedObject.object_type.name, - 'name': get_name(obj), - 'url': get_attribute(obj, 'url'), - 'changed_on': get_attribute(obj, 'changed_on'), - 'created_by': get_attribute(obj, 'created_by_fk'), - 'creator': get_creator(obj), - } - for obj in query if get_attribute(obj, 'id') + types = [ + type_ + for type_ in request.args.get('types', '').split(',') + if type_ ] - return json_success(json.dumps(objects, default=utils.core.json_int_dttm_ser)) + results = [] + + # dashboards + if not types or 'dashboard' in types: + dashboards = ( + db.session.query(Dashboard) + .join( + TaggedObject, + and_( + TaggedObject.object_id == Dashboard.id, + TaggedObject.object_type == ObjectTypes.dashboard, + ), + ) + .join(Tag, TaggedObject.tag_id == Tag.id) + .filter(Tag.name.in_(tags)) + ) + results.extend( + { + 'id': obj.id, + 'type': ObjectTypes.dashboard.name, + 'name': obj.dashboard_title, + 'url': obj.url, + 'changed_on': obj.changed_on, + 'created_by': obj.created_by_fk, + 'creator': obj.creator(), + } for obj in dashboards + ) + + # charts + if not types or 'chart' in types: + charts = ( + db.session.query(Slice) + .join( + TaggedObject, + and_( + TaggedObject.object_id == Slice.id, + TaggedObject.object_type == ObjectTypes.chart, + ), + ) + .join(Tag, TaggedObject.tag_id == Tag.id) + .filter(Tag.name.in_(tags)) + ) + results.extend( + { + 'id': obj.id, + 'type': ObjectTypes.chart.name, + 'name': obj.slice_name, + 'url': obj.url, + 'changed_on': obj.changed_on, + 'created_by': obj.created_by_fk, + 'creator': obj.creator(), + } for obj in charts + ) + + # saved queries + if not types or 'query' in types: + saved_queries = ( + db.session.query(SavedQuery) + .join( + TaggedObject, + and_( + TaggedObject.object_id == SavedQuery.id, + TaggedObject.object_type == ObjectTypes.query, + ), + ) + .join(Tag, TaggedObject.tag_id == Tag.id) + .filter(Tag.name.in_(tags)) + ) + results.extend( + { + 'id': obj.id, + 'type': ObjectTypes.query.name, + 'name': obj.label, + 'url': obj.url, + 'changed_on': obj.changed_on, + 'created_by': obj.created_by_fk, + 'creator': obj.creator(), + } for obj in saved_queries + ) + + return json_success(json.dumps(results, default=utils.core.json_int_dttm_ser)) app.url_map.converters['object_type'] = ObjectTypeConverter From ad7cc01922fdee70dbcc45efbd629e29cf818827 Mon Sep 17 00:00:00 2001 From: Thomas Wang <17309187+datability-io@users.noreply.github.com> Date: Mon, 8 Apr 2019 15:45:39 -0700 Subject: [PATCH 22/38] increment version numbers (#7245) * increment version numbers * increment version numbers * increment nvd3 --- superset/assets/package-lock.json | 54 +++++++++++++++---------------- superset/assets/package.json | 16 ++++----- 2 files changed, 35 insertions(+), 35 deletions(-) diff --git a/superset/assets/package-lock.json b/superset/assets/package-lock.json index cf656671b..41ec051ae 100644 --- a/superset/assets/package-lock.json +++ b/superset/assets/package-lock.json @@ -2220,9 +2220,9 @@ } }, "@superset-ui/chart": { - "version": "0.10.8", - "resolved": "https://registry.npmjs.org/@superset-ui/chart/-/chart-0.10.8.tgz", - "integrity": "sha512-O/i0o/Y5E2tlAEOWHgZaEpnXDqX25Q/xcrgMKOBjbcCUeGMysekvcs7hMkYVI/vTt15sJY3R318kXKNWX6RkKA==", + "version": "0.11.1", + "resolved": "https://registry.npmjs.org/@superset-ui/chart/-/chart-0.11.1.tgz", + "integrity": "sha512-TnXRn/FrlWuTQbpM3TLnSirNn7rKu43TceMt6hcnxbAlLYDIs75YQvBkkjtDYjeuIbkNrpZdfaIQuuTXV3InRw==", "requires": { "@types/react": "^16.7.17", "@types/react-loadable": "^5.4.2", @@ -2232,9 +2232,9 @@ } }, "@superset-ui/color": { - "version": "0.10.9", - "resolved": "https://registry.npmjs.org/@superset-ui/color/-/color-0.10.9.tgz", - "integrity": "sha512-/X+RFQtb//su5r7RIHZdDjy3dx+OZ2VOseqfeJXFjyte/4vLzSix3cA5Rj0iI8hU1tJz5mwt4q87M/MA4wDX6g==", + "version": "0.11.0", + "resolved": "https://registry.npmjs.org/@superset-ui/color/-/color-0.11.0.tgz", + "integrity": "sha512-/eAYiUSu9GuEVTxylfOC0GV8OyJ3d6oEMIMGLfAztMOFu/5LFHEWPjJJS9D1eA9h1EaEo3nfPi0cSAtqE8oI5w==", "requires": { "@types/d3-scale": "^2.0.2", "d3-scale": "^2.1.2" @@ -2257,17 +2257,17 @@ } }, "@superset-ui/core": { - "version": "0.10.6", - "resolved": "https://registry.npmjs.org/@superset-ui/core/-/core-0.10.6.tgz", - "integrity": "sha512-BYKkz1zyV3PykuvIM/3XqNk7dfTEBCvR+Kxd//6w5Z2/JPlnhp+t4Nm7s4g6tLdtW76LcL7JwvBO14wMSfsNvw==", + "version": "0.11.0", + "resolved": "https://registry.npmjs.org/@superset-ui/core/-/core-0.11.0.tgz", + "integrity": "sha512-Dmq+bbsZFlJka677y0kBGgpW+HL5VVZ1RRYNXIUgoVH0+dHmUprJshhSv3i4pzgNrwq1/1VEXwqQtrGboxIcZw==", "requires": { "lodash": "^4.17.11" } }, "@superset-ui/dimension": { - "version": "0.10.4", - "resolved": "https://registry.npmjs.org/@superset-ui/dimension/-/dimension-0.10.4.tgz", - "integrity": "sha512-+VDPb2bOJS6H/zxh/MC+WSgXemQjPx8GMNu+LKbbgaBYCg1xyo/ldqPdOCo+Gr4YgjUwyCNfxRG4W7Fzd1NvfA==" + "version": "0.11.0", + "resolved": "https://registry.npmjs.org/@superset-ui/dimension/-/dimension-0.11.0.tgz", + "integrity": "sha512-o7I3tFRphK5KnOJPANTWbgB2LZCGRKUTQX667lsBEKpGFRwz4fbGHZlEIHx6o9kb1PEhc44XJ94hs7mMGr1FDg==" }, "@superset-ui/legacy-plugin-chart-calendar": { "version": "0.10.0", @@ -2594,9 +2594,9 @@ } }, "@superset-ui/legacy-preset-chart-nvd3": { - "version": "0.10.7", - "resolved": "https://registry.npmjs.org/@superset-ui/legacy-preset-chart-nvd3/-/legacy-preset-chart-nvd3-0.10.7.tgz", - "integrity": "sha512-Zhkyz3NNtKHeQ8SwU64aKCJmaOuf63/5wm2/Iu+nV8U/8KcnqFpQklBwjOlURt7tiKe7Jx+HGT3k0RMX/16rYg==", + "version": "0.10.9", + "resolved": "https://registry.npmjs.org/@superset-ui/legacy-preset-chart-nvd3/-/legacy-preset-chart-nvd3-0.10.9.tgz", + "integrity": "sha512-2XLyPqDXFhDYGCBeEueGabF9Leh+X5bzR/AaXP3RQdfpwFSty0fo9IIAbJuq/3+0re/WuGW3xdN5h/No/n66qQ==", "requires": { "@data-ui/xy-chart": "^0.0.75", "d3": "^3.5.17", @@ -2612,18 +2612,18 @@ } }, "@superset-ui/number-format": { - "version": "0.10.1", - "resolved": "https://registry.npmjs.org/@superset-ui/number-format/-/number-format-0.10.1.tgz", - "integrity": "sha512-/WtYcngI/RTPaP+WapWha1Ay5F1GULjeop8JVZWxWqmiLVp9eReRmm0YlCiJ2inR7K5y9t/+h0XepbW4BTJs3Q==", + "version": "0.11.0", + "resolved": "https://registry.npmjs.org/@superset-ui/number-format/-/number-format-0.11.0.tgz", + "integrity": "sha512-DDK0opkYXOL42M2ONzrQVauAAO/15cP08dNy5DAP0x7O7vLQQ/DrhXbGW/GwHsL8tMRdCXyJn7gN7rodtBkDJg==", "requires": { "@types/d3-format": "^1.3.0", "d3-format": "^1.3.2" } }, "@superset-ui/time-format": { - "version": "0.10.1", - "resolved": "https://registry.npmjs.org/@superset-ui/time-format/-/time-format-0.10.1.tgz", - "integrity": "sha512-p/6UJ7KhwuwlaqcNYYTe4euosm82Y1YhEQEAMIRvEiWh9wdh1hCSYa+JvAoW2l6T9z9yCuVorV5J4bhU0587FA==", + "version": "0.11.2", + "resolved": "https://registry.npmjs.org/@superset-ui/time-format/-/time-format-0.11.2.tgz", + "integrity": "sha512-9VSdOyLmcj6v7HConYhuh1/r+nJAtGJzaJRgIGYIE+0j7hu6XAr8u4JOwJO/KUXY7oT7ppqy3hN4UJqN9w25ZA==", "requires": { "@types/d3-time": "^1.0.9", "@types/d3-time-format": "^2.1.0", @@ -2632,9 +2632,9 @@ } }, "@superset-ui/translation": { - "version": "0.10.0", - "resolved": "https://registry.npmjs.org/@superset-ui/translation/-/translation-0.10.0.tgz", - "integrity": "sha512-fSrPgk9b6so2IMzGAUFEHkQHMYURCLNx9gIqJ+u/PG1fgQ9bXpDoYbUmI2xfAVo7cRcudDRyeG+PuLquqetX1Q==", + "version": "0.11.0", + "resolved": "https://registry.npmjs.org/@superset-ui/translation/-/translation-0.11.0.tgz", + "integrity": "sha512-enL7Qw/kVfUziZrQQcNorvhJBmJBmXCxq8rFjPtsNiPE5sRgxSb8ac5SNLBgD8pW1IbJxjoXlHFoufIPeXkiCQ==", "requires": { "jed": "^1.1.1" } @@ -2747,9 +2747,9 @@ "integrity": "sha512-iHI60IbyfQilNubmxsq4zqSjdynlmc2Q/QvH9kjzg9+CCYVVzq1O6tc7VBzSygIwnmOt07w80IG6HDQvjv3Liw==" }, "@types/webpack": { - "version": "4.4.26", - "resolved": "https://registry.npmjs.org/@types/webpack/-/webpack-4.4.26.tgz", - "integrity": "sha512-vs8LjgEZUQTBxotXbMf8s4jgykozkqjv6P0JRi+1BLh0n7LQUkMXfvsoPb5U/dBL1ay5Lu0c46G6FRmAZBhAUA==", + "version": "4.4.27", + "resolved": "https://registry.npmjs.org/@types/webpack/-/webpack-4.4.27.tgz", + "integrity": "sha512-xSll/4UXnLQ0xjdAoTRIFxA6NPC2abJ8nHxRH6SqTymHrfGCc8er7qH0npwCP8q3VFoJh2Hjz1wH8oTjwx9/jQ==", "requires": { "@types/anymatch": "*", "@types/node": "*", diff --git a/superset/assets/package.json b/superset/assets/package.json index 283bee87d..170b75448 100644 --- a/superset/assets/package.json +++ b/superset/assets/package.json @@ -48,11 +48,11 @@ "homepage": "https://superset.apache.org/", "dependencies": { "@data-ui/sparkline": "^0.0.54", - "@superset-ui/chart": "^0.10.2", - "@superset-ui/color": "^0.10.9", + "@superset-ui/chart": "^0.11.1", + "@superset-ui/color": "^0.11.0", "@superset-ui/connection": "^0.11.0", - "@superset-ui/core": "^0.10.0", - "@superset-ui/dimension": "^0.10.0", + "@superset-ui/core": "^0.11.0", + "@superset-ui/dimension": "^0.11.0", "@superset-ui/legacy-plugin-chart-calendar": "^0.10.0", "@superset-ui/legacy-plugin-chart-chord": "^0.10.0", "@superset-ui/legacy-plugin-chart-country-map": "^0.10.0", @@ -76,10 +76,10 @@ "@superset-ui/legacy-plugin-chart-word-cloud": "^0.10.0", "@superset-ui/legacy-plugin-chart-world-map": "^0.10.0", "@superset-ui/legacy-preset-chart-big-number": "^0.10.4", - "@superset-ui/legacy-preset-chart-nvd3": "^0.10.6", - "@superset-ui/number-format": "^0.10.1", - "@superset-ui/time-format": "^0.10.1", - "@superset-ui/translation": "^0.10.0", + "@superset-ui/legacy-preset-chart-nvd3": "^0.10.9", + "@superset-ui/number-format": "^0.11.0", + "@superset-ui/time-format": "^0.11.2", + "@superset-ui/translation": "^0.11.0", "@vx/responsive": "0.0.172", "abortcontroller-polyfill": "^1.1.9", "bootstrap": "^3.3.6", From 23ef41ffaa1e936237c86ed4cb100d8bc87df755 Mon Sep 17 00:00:00 2001 From: Beto Dealmeida Date: Mon, 8 Apr 2019 16:43:56 -0700 Subject: [PATCH 23/38] Fix spinning in Welcome page (#7247) --- superset/views/tags.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/superset/views/tags.py b/superset/views/tags.py index 65cbbc966..3be876912 100644 --- a/superset/views/tags.py +++ b/superset/views/tags.py @@ -219,7 +219,7 @@ class TagView(BaseSupersetView): 'id': obj.id, 'type': ObjectTypes.query.name, 'name': obj.label, - 'url': obj.url, + 'url': obj.url(), 'changed_on': obj.changed_on, 'created_by': obj.created_by_fk, 'creator': obj.creator(), From 821860ea7db5125cb267948d1991969399bc1de0 Mon Sep 17 00:00:00 2001 From: Beto Dealmeida Date: Tue, 9 Apr 2019 08:44:44 -0700 Subject: [PATCH 24/38] Fix partition query in Presto (#7250) --- superset/db_engine_specs.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/superset/db_engine_specs.py b/superset/db_engine_specs.py index 9eee7c52f..620ac4ee9 100644 --- a/superset/db_engine_specs.py +++ b/superset/db_engine_specs.py @@ -953,7 +953,6 @@ class PrestoEngineSpec(BaseEngineSpec): that determines if that field should be sorted in descending order :type order_by: list of (str, bool) tuples - :param filters: a list of filters to apply :param filters: dict of field name and filter value combinations """ limit_clause = 'LIMIT {}'.format(limit) if limit else '' @@ -972,7 +971,8 @@ class PrestoEngineSpec(BaseEngineSpec): where_clause = 'WHERE ' + ' AND '.join(l) sql = textwrap.dedent(f"""\ - SHOW PARTITIONS FROM {table_name} + SELECT * FROM "{table_name}$partitions" + {where_clause} {order_by_clause} {limit_clause} From 51544212b654181a7fc968a6cda4f52670f7a48e Mon Sep 17 00:00:00 2001 From: Beto Dealmeida Date: Tue, 9 Apr 2019 09:40:19 -0700 Subject: [PATCH 25/38] Add link to documentation (#7251) --- superset/config.py | 2 ++ superset/templates/appbuilder/navbar_right.html | 12 ++++++++++++ 2 files changed, 14 insertions(+) diff --git a/superset/config.py b/superset/config.py index 96ccb09d0..b402fec9a 100644 --- a/superset/config.py +++ b/superset/config.py @@ -592,6 +592,8 @@ WEBDRIVER_BASEURL = 'http://0.0.0.0:8080/' # Send user to a link where they can report bugs BUG_REPORT_URL = None +# Send user to a link where they can read more about Superset +DOCUMENTATION_URL = None # What is the Last N days relative in the time selector to: # 'today' means it is midnight (00:00:00) of today in the local timezone diff --git a/superset/templates/appbuilder/navbar_right.html b/superset/templates/appbuilder/navbar_right.html index 878e93764..73c545e39 100644 --- a/superset/templates/appbuilder/navbar_right.html +++ b/superset/templates/appbuilder/navbar_right.html @@ -18,6 +18,7 @@ #} {% set bug_report_url = appbuilder.app.config.get('BUG_REPORT_URL') %} +{% set documentation_url = appbuilder.app.config.get('DOCUMENTATION_URL') %} {% set locale = session['locale'] %} {% if not locale %} {% set locale = 'en' %} @@ -35,6 +36,17 @@ {% endif %} +{% if documentation_url %} +
  • + +   + +
  • +{% endif %} {% if bug_report_url %}
  • Date: Tue, 9 Apr 2019 11:05:44 -0700 Subject: [PATCH 26/38] Improve cache (#7227) * Improve cache * Improve code, add docs * Simplify flow * Update docstring --- superset/utils/decorators.py | 63 +++++++++++++++++++++++------------- 1 file changed, 40 insertions(+), 23 deletions(-) diff --git a/superset/utils/decorators.py b/superset/utils/decorators.py index 23b15a5d6..911ab7f92 100644 --- a/superset/utils/decorators.py +++ b/superset/utils/decorators.py @@ -47,12 +47,13 @@ def etag_cache(max_age, check_perms=bool): """ A decorator for caching views and handling etag conditional requests. - The decorator caches the response, returning headers for etag and last - modified. If the client makes a request that matches, the server will - return a "304 Not Mofified" status. + The decorator adds headers to GET requests that help with caching: Last- + Modified, Expires and ETag. It also handles conditional requests, when the + client send an If-Matches header. - If no cache is set, the decorator will still set the ETag header, and - handle conditional requests. + If a cache is set, the decorator will cache GET responses, bypassing the + dataframe serialization. POST requests will still benefit from the + dataframe cache for requests that produce the same SQL. """ def decorator(f): @@ -61,30 +62,46 @@ def etag_cache(max_age, check_perms=bool): # check if the user can access the resource check_perms(*args, **kwargs) - try: - # build the cache key from the function arguments and any other - # additional GET arguments (like `form_data`, eg). - key_args = list(args) - key_kwargs = kwargs.copy() - key_kwargs.update(request.args) - cache_key = wrapper.make_cache_key(f, *key_args, **key_kwargs) - response = cache.get(cache_key) - except Exception: # pylint: disable=broad-except - if app.debug: - raise - logging.exception('Exception possibly due to cache backend.') - response = None + # for POST requests we can't set cache headers, use the response + # cache nor use conditional requests; this will still use the + # dataframe cache in `superset/viz.py`, though. + if request.method == 'POST': + return f(*args, **kwargs) - if response is None or request.method == 'POST': + response = None + if cache: + try: + # build the cache key from the function arguments and any + # other additional GET arguments (like `form_data`, eg). + key_args = list(args) + key_kwargs = kwargs.copy() + key_kwargs.update(request.args) + cache_key = wrapper.make_cache_key(f, *key_args, **key_kwargs) + response = cache.get(cache_key) + except Exception: # pylint: disable=broad-except + if app.debug: + raise + logging.exception('Exception possibly due to cache backend.') + + # if no response was cached, compute it using the wrapped function + if response is None: response = f(*args, **kwargs) + + # add headers for caching: Last Modified, Expires and ETag response.cache_control.public = True response.last_modified = datetime.utcnow() expiration = max_age if max_age != 0 else FAR_FUTURE - response.expires = response.last_modified + timedelta(seconds=expiration) + response.expires = \ + response.last_modified + timedelta(seconds=expiration) response.add_etag() - try: - cache.set(cache_key, response, timeout=max_age) - except Exception: # pylint: disable=broad-except + + # if we have a cache, store the response from the request + if cache: + try: + cache.set(cache_key, response, timeout=max_age) + except Exception: # pylint: disable=broad-except + if app.debug: + raise logging.exception('Exception possibly due to cache backend.') return response.make_conditional(request) From 7c80cf58ebed008e17cb1a9e69142c4d43f11d31 Mon Sep 17 00:00:00 2001 From: Beto Dealmeida Date: Tue, 9 Apr 2019 11:22:28 -0700 Subject: [PATCH 27/38] Fix refresh frequency (#7248) * Fix refresh frequency * Fix unit tests --- .../components/HeaderActionsDropdown_spec.jsx | 8 ++++---- superset/assets/src/dashboard/components/Header.jsx | 2 ++ .../dashboard/components/HeaderActionsDropdown.jsx | 12 +++++++----- .../assets/src/dashboard/reducers/dashboardState.js | 6 +++++- 4 files changed, 18 insertions(+), 10 deletions(-) diff --git a/superset/assets/spec/javascripts/dashboard/components/HeaderActionsDropdown_spec.jsx b/superset/assets/spec/javascripts/dashboard/components/HeaderActionsDropdown_spec.jsx index cd1c0156c..f89c9ed94 100644 --- a/superset/assets/spec/javascripts/dashboard/components/HeaderActionsDropdown_spec.jsx +++ b/superset/assets/spec/javascripts/dashboard/components/HeaderActionsDropdown_spec.jsx @@ -71,9 +71,9 @@ describe('HeaderActionsDropdown', () => { expect(wrapper.find(MenuItem)).toHaveLength(1); }); - it('should render the RefreshIntervalModal', () => { + it('should not render the RefreshIntervalModal', () => { const wrapper = setup(overrideProps); - expect(wrapper.find(RefreshIntervalModal)).toHaveLength(1); + expect(wrapper.find(RefreshIntervalModal)).toHaveLength(0); }); it('should render the URLShortLinkModal', () => { @@ -105,9 +105,9 @@ describe('HeaderActionsDropdown', () => { expect(wrapper.find(MenuItem)).toHaveLength(2); }); - it('should render the RefreshIntervalModal', () => { + it('should not render the RefreshIntervalModal', () => { const wrapper = setup(overrideProps); - expect(wrapper.find(RefreshIntervalModal)).toHaveLength(1); + expect(wrapper.find(RefreshIntervalModal)).toHaveLength(0); }); it('should render the URLShortLinkModal', () => { diff --git a/superset/assets/src/dashboard/components/Header.jsx b/superset/assets/src/dashboard/components/Header.jsx index 366efbe3d..92d3b1df6 100644 --- a/superset/assets/src/dashboard/components/Header.jsx +++ b/superset/assets/src/dashboard/components/Header.jsx @@ -215,6 +215,7 @@ class Header extends React.PureComponent { colorScheme, filters, dashboardInfo, + refreshFrequency, } = this.props; const scale = CategoricalColorNamespace.getScale( @@ -231,6 +232,7 @@ class Header extends React.PureComponent { label_colors: labelColors, dashboard_title: dashboardTitle, default_filters: safeStringify(filters), + refresh_frequency: refreshFrequency, }; // make sure positions data less than DB storage limitation: diff --git a/superset/assets/src/dashboard/components/HeaderActionsDropdown.jsx b/superset/assets/src/dashboard/components/HeaderActionsDropdown.jsx index 5fc9d0ede..5aa468b74 100644 --- a/superset/assets/src/dashboard/components/HeaderActionsDropdown.jsx +++ b/superset/assets/src/dashboard/components/HeaderActionsDropdown.jsx @@ -177,11 +177,13 @@ class HeaderActionsDropdown extends React.PureComponent { {t('Force refresh dashboard')} - {t('Set auto-refresh interval')}} - /> + {editMode && ( + {t('Set auto-refresh interval')}} + /> + )} {editMode && ( {t('Edit dashboard metadata')} diff --git a/superset/assets/src/dashboard/reducers/dashboardState.js b/superset/assets/src/dashboard/reducers/dashboardState.js index 007f63a28..830e2a751 100644 --- a/superset/assets/src/dashboard/reducers/dashboardState.js +++ b/superset/assets/src/dashboard/reducers/dashboardState.js @@ -162,7 +162,11 @@ export default function dashboardStateReducer(state = {}, action) { return { ...state, hasUnsavedChanges }; }, [SET_REFRESH_FREQUENCY]() { - return { ...state, refreshFrequency: action.refreshFrequency }; + return { + ...state, + refreshFrequency: action.refreshFrequency, + hasUnsavedChanges: true, + }; }, }; From 2a67e8e457bcf07d9f07ab56ddf232d057c4c838 Mon Sep 17 00:00:00 2001 From: Kim Truong <47833996+khtruong@users.noreply.github.com> Date: Tue, 9 Apr 2019 15:46:47 -0700 Subject: [PATCH 28/38] fix: Handle rendering a single point (#7256) * fix: Handle rendering a single point * fix: typo --- .../deckgl/layers/common_spec.jsx | 63 ++++++++++++++++++- .../visualizations/deckgl/layers/common.jsx | 42 ++++++++++++- 2 files changed, 102 insertions(+), 3 deletions(-) diff --git a/superset/assets/spec/javascripts/visualizations/deckgl/layers/common_spec.jsx b/superset/assets/spec/javascripts/visualizations/deckgl/layers/common_spec.jsx index a2a6ef3f5..c82eeeea4 100644 --- a/superset/assets/spec/javascripts/visualizations/deckgl/layers/common_spec.jsx +++ b/superset/assets/spec/javascripts/visualizations/deckgl/layers/common_spec.jsx @@ -17,7 +17,7 @@ * under the License. */ import { max } from 'd3-array'; -import { getAggFunc } from '../../../../../src/visualizations/deckgl/layers/common'; +import { getAggFunc, getBounds } from '../../../../../src/visualizations/deckgl/layers/common'; describe('deckgl layers common', () => { it('getAggFunc', () => { @@ -46,4 +46,65 @@ describe('deckgl layers common', () => { expect(getAggFunc('p95', accessor)(arr)).toEqual(2.9); expect(getAggFunc('p99', accessor)(arr)).toEqual(2.98); }); + + describe('getBounds', () => { + it('should return valid bounds for multiple points', () => { + const points = [ + [0, 20], + [5, 25], + [10, 15], + ]; + expect(getBounds(points)).toEqual([ + [0, 15], + [10, 25], + ]); + }); + it('should return valid bounds for single latitude point', () => { + const points = [ + [0, 0], + [5, 0], + ]; + expect(getBounds(points)).toEqual([ + [0, -0.25], + [5, 0.25], + ]); + }); + it('should return valid bounds for single longitude point', () => { + const points = [ + [0, 0], + [0, 5], + ]; + expect(getBounds(points)).toEqual([ + [-0.25, 0], + [0.25, 5], + ]); + }); + it('should return valid bounds for single point', () => { + const points = [ + [0, 0], + ]; + expect(getBounds(points)).toEqual([ + [-0.25, -0.25], + [0.25, 0.25], + ]); + }); + it('should return valid bounds for point 90, 180', () => { + const points = [ + [180, 90], + ]; + expect(getBounds(points)).toEqual([ + [179.75, 89.75], + [180, 90], + ]); + }); + it('should return valid bounds for point -90, -180', () => { + const points = [ + [-180, -90], + ]; + expect(getBounds(points)).toEqual([ + [-180, -90], + [-179.75, -89.75], + ]); + }); + }); }); diff --git a/superset/assets/src/visualizations/deckgl/layers/common.jsx b/superset/assets/src/visualizations/deckgl/layers/common.jsx index 7adf3b789..aaee55361 100644 --- a/superset/assets/src/visualizations/deckgl/layers/common.jsx +++ b/superset/assets/src/visualizations/deckgl/layers/common.jsx @@ -20,12 +20,50 @@ import { fitBounds } from 'viewport-mercator-project'; import * as d3array from 'd3-array'; import sandboxedEval from '../../../modules/sandbox'; +const PADDING = 0.25; +const GEO_BOUNDS = { + LAT_MIN: -90, + LAT_MAX: 90, + LNG_MIN: -180, + LNG_MAX: 180, +}; + +/** + * Get the latitude bounds if latitude is a single coordinate + * @param latExt Latitude range + */ +function getLatBoundsForSingleCoordinate(latExt) { + const latMin = latExt[0] - PADDING < GEO_BOUNDS.LAT_MIN + ? GEO_BOUNDS.LAT_MIN + : latExt[0] - PADDING; + const latMax = latExt[1] + PADDING > GEO_BOUNDS.LAT_MAX + ? GEO_BOUNDS.LAT_MAX + : latExt[1] + PADDING; + return [latMin, latMax]; +} + +/** + * Get the longitude bounds if longitude is a single coordinate + * @param lngExt Longitude range + */ +function getLngBoundsForSingleCoordinate(lngExt) { + const lngMin = lngExt[0] - PADDING < GEO_BOUNDS.LNG_MIN + ? GEO_BOUNDS.LNG_MIN + : lngExt[0] - PADDING; + const lngMax = lngExt[1] + PADDING > GEO_BOUNDS.LNG_MAX + ? GEO_BOUNDS.LNG_MAX + : lngExt[1] + PADDING; + return [lngMin, lngMax]; +} + export function getBounds(points) { const latExt = d3array.extent(points, d => d[1]); const lngExt = d3array.extent(points, d => d[0]); + const latBounds = latExt[0] === latExt[1] ? getLatBoundsForSingleCoordinate(latExt) : latExt; + const lngBounds = lngExt[0] === lngExt[1] ? getLngBoundsForSingleCoordinate(lngExt) : lngExt; return [ - [lngExt[0], latExt[0]], - [lngExt[1], latExt[1]], + [lngBounds[0], latBounds[0]], + [lngBounds[1], latBounds[1]], ]; } From 078a9774ce43238e8d46050f1e6cca6399a58aac Mon Sep 17 00:00:00 2001 From: Beto Dealmeida Date: Tue, 9 Apr 2019 16:54:37 -0700 Subject: [PATCH 29/38] Pass viz type to GET requests (#7255) * Pass viz type to GET requests * Fix integration tests --- .../cypress/integration/dashboard/controls.js | 16 ++++++++-------- .../cypress/integration/dashboard/edit_mode.js | 3 ++- .../cypress/integration/dashboard/filter.js | 3 ++- .../assets/cypress/integration/dashboard/load.js | 3 ++- .../assets/cypress/integration/dashboard/save.js | 3 ++- superset/assets/src/explore/exploreUtils.js | 3 ++- superset/views/utils.py | 4 ++-- 7 files changed, 20 insertions(+), 15 deletions(-) diff --git a/superset/assets/cypress/integration/dashboard/controls.js b/superset/assets/cypress/integration/dashboard/controls.js index 3e218d2ca..fe5581590 100644 --- a/superset/assets/cypress/integration/dashboard/controls.js +++ b/superset/assets/cypress/integration/dashboard/controls.js @@ -32,18 +32,18 @@ export default () => describe('top-level controls', () => { cy.get('#app').then((data) => { const bootstrapData = JSON.parse(data[0].dataset.bootstrap); const dashboard = bootstrapData.dashboard_data; - const sliceIds = dashboard.slices.map(slice => (slice.slice_id)); mapId = dashboard.slices.find(slice => (slice.form_data.viz_type === 'world_map')).slice_id; - sliceIds - .forEach((id) => { - const sliceRequest = `getJson_${id}`; + dashboard.slices + .forEach((slice) => { + const sliceRequest = `getJson_${slice.slice_id}`; sliceRequests.push(`@${sliceRequest}`); - cy.route('GET', `/superset/explore_json/?form_data={"slice_id":${id}}`).as(sliceRequest); + const formData = `{"slice_id":${slice.slice_id},"viz_type":"${slice.form_data.viz_type}"}`; + cy.route('GET', `/superset/explore_json/?form_data=${formData}`).as(sliceRequest); - const forceRefresh = `getJson_${id}_force`; + const forceRefresh = `postJson_${slice.slice_id}_force`; forceRefreshRequests.push(`@${forceRefresh}`); - cy.route('POST', `/superset/explore_json/?form_data={"slice_id":${id}}&force=true`).as(forceRefresh); + cy.route('POST', `/superset/explore_json/?form_data={"slice_id":${slice.slice_id}}&force=true`).as(forceRefresh); }); }); }); @@ -69,7 +69,7 @@ export default () => describe('top-level controls', () => { .parent() .should('have.class', 'disabled'); - cy.wait(`@getJson_${mapId}_force`); + cy.wait(`@postJson_${mapId}_force`); cy.get('#save-dash-split-button').trigger('click'); cy.contains('Force refresh dashboard').parent().not('have.class', 'disabled'); }); diff --git a/superset/assets/cypress/integration/dashboard/edit_mode.js b/superset/assets/cypress/integration/dashboard/edit_mode.js index d9395d276..280b6aae4 100644 --- a/superset/assets/cypress/integration/dashboard/edit_mode.js +++ b/superset/assets/cypress/integration/dashboard/edit_mode.js @@ -28,7 +28,8 @@ export default () => describe('edit mode', () => { const bootstrapData = JSON.parse(data[0].dataset.bootstrap); const dashboard = bootstrapData.dashboard_data; const boxplotChartId = dashboard.slices.find(slice => (slice.form_data.viz_type === 'box_plot')).slice_id; - const boxplotRequest = `/superset/explore_json/?form_data={"slice_id":${boxplotChartId}}`; + const formData = `{"slice_id":${boxplotChartId},"viz_type":"box_plot"}`; + const boxplotRequest = `/superset/explore_json/?form_data=${formData}`; cy.route('GET', boxplotRequest).as('boxplotRequest'); }); diff --git a/superset/assets/cypress/integration/dashboard/filter.js b/superset/assets/cypress/integration/dashboard/filter.js index 6ec1c9261..f37c8c849 100644 --- a/superset/assets/cypress/integration/dashboard/filter.js +++ b/superset/assets/cypress/integration/dashboard/filter.js @@ -39,7 +39,8 @@ export default () => describe('dashboard filter', () => { it('should apply filter', () => { const aliases = []; - const filterRoute = `/superset/explore_json/?form_data={"slice_id":${filterId}}`; + const formData = `{"slice_id":${filterId},"viz_type":"filter_box"}`; + const filterRoute = `/superset/explore_json/?form_data=${formData}`; cy.route('GET', filterRoute).as('fetchFilter'); cy.wait('@fetchFilter'); sliceIds diff --git a/superset/assets/cypress/integration/dashboard/load.js b/superset/assets/cypress/integration/dashboard/load.js index 0dbe1ff14..79daa3042 100644 --- a/superset/assets/cypress/integration/dashboard/load.js +++ b/superset/assets/cypress/integration/dashboard/load.js @@ -34,7 +34,8 @@ export default () => describe('load', () => { // then define routes and create alias for each requests slices.forEach((slice) => { const alias = `getJson_${slice.slice_id}`; - cy.route('GET', `/superset/explore_json/?form_data={"slice_id":${slice.slice_id}}`).as(alias); + const formData = `{"slice_id":${slice.slice_id},"viz_type":"${slice.form_data.viz_type}"}`; + cy.route('GET', `/superset/explore_json/?form_data=${formData}`).as(alias); aliases.push(`@${alias}`); }); }); diff --git a/superset/assets/cypress/integration/dashboard/save.js b/superset/assets/cypress/integration/dashboard/save.js index d144a71ca..1d26ac219 100644 --- a/superset/assets/cypress/integration/dashboard/save.js +++ b/superset/assets/cypress/integration/dashboard/save.js @@ -56,7 +56,8 @@ export default () => describe('save', () => { cy.wait('@copyRequest'); // should have box_plot chart - const boxplotRequest = `/superset/explore_json/?form_data={"slice_id":${boxplotChartId}}`; + const formData = `{"slice_id":${boxplotChartId},"viz_type":"box_plot"}`; + const boxplotRequest = `/superset/explore_json/?form_data=${formData}`; cy.route('GET', boxplotRequest).as('boxplotRequest'); cy.wait('@boxplotRequest'); cy.get('.grid-container .box_plot').should('be.exist'); diff --git a/superset/assets/src/explore/exploreUtils.js b/superset/assets/src/explore/exploreUtils.js index 478593338..eabedbabb 100644 --- a/superset/assets/src/explore/exploreUtils.js +++ b/superset/assets/src/explore/exploreUtils.js @@ -119,10 +119,11 @@ export function getExploreUrlAndPayload({ // Building the querystring (search) part of the URI const search = uri.search(true); - const { slice_id, extra_filters, adhoc_filters } = formData; + const { slice_id, extra_filters, adhoc_filters, viz_type } = formData; if (slice_id) { const form_data = { slice_id }; if (method === 'GET') { + form_data.viz_type = viz_type; if (extra_filters && extra_filters.length) { form_data.extra_filters = extra_filters; } diff --git a/superset/views/utils.py b/superset/views/utils.py index 4318141e3..74171d1e4 100644 --- a/superset/views/utils.py +++ b/superset/views/utils.py @@ -153,8 +153,8 @@ def get_form_data(slice_id=None, use_slice_data=False): slice_id = form_data.get('slice_id') or slice_id slc = None - # Check if form data only contains slice_id and additional filters - valid_keys = ['slice_id', 'extra_filters', 'adhoc_filters'] + # Check if form data only contains slice_id, additional filters and viz type + valid_keys = ['slice_id', 'extra_filters', 'adhoc_filters', 'viz_type'] valid_slice_id = all(key in valid_keys for key in form_data) # Include the slice_form_data if request from explore or slice calls From 05afe1b8c23cfd8ce26a628d2a393e8cfc8da91a Mon Sep 17 00:00:00 2001 From: Beto Dealmeida Date: Tue, 9 Apr 2019 17:39:24 -0700 Subject: [PATCH 30/38] Bump pydruid to 0.5.2 (#7260) --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index 4b6b4f56e..bab2c19fd 100644 --- a/requirements.txt +++ b/requirements.txt @@ -49,7 +49,7 @@ pathlib2==2.3.0 polyline==1.3.2 py==1.7.0 # via retry pycparser==2.19 # via cffi -pydruid==0.5.0 +pydruid==0.5.2 pyjwt==1.7.1 # via flask-appbuilder python-dateutil==2.6.1 python-editor==1.0.3 # via alembic From 984b04d1e5251702170af143ebd373393811af8c Mon Sep 17 00:00:00 2001 From: Beto Dealmeida Date: Tue, 9 Apr 2019 19:35:13 -0700 Subject: [PATCH 31/38] Add extra bucket when max outside range (#7264) --- superset/assets/src/visualizations/deckgl/utils.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/superset/assets/src/visualizations/deckgl/utils.js b/superset/assets/src/visualizations/deckgl/utils.js index 76ed54ddd..b2b130a48 100644 --- a/superset/assets/src/visualizations/deckgl/utils.js +++ b/superset/assets/src/visualizations/deckgl/utils.js @@ -38,7 +38,8 @@ export function getBreakPoints({ const precision = delta === 0 ? 0 : Math.max(0, Math.ceil(Math.log10(1 / delta))); - return Array(numBuckets + 1) + const extraBucket = maxValue > maxValue.toFixed(precision) ? 1 : 0; + return Array(numBuckets + 1 + extraBucket) .fill() .map((_, i) => (minValue + i * delta).toFixed(precision)); } From e505e326b22b47c2f25ec6c69310b93ab5d28fda Mon Sep 17 00:00:00 2001 From: Beto Dealmeida Date: Tue, 9 Apr 2019 20:39:24 -0700 Subject: [PATCH 32/38] Revert "Fix issues around Database permissions (#7009)" (#7267) This reverts commit f5274a9c7f437dcf9359f8dc5fdd056c23bf03b8. --- superset/security.py | 12 +++--------- superset/views/core.py | 9 --------- 2 files changed, 3 insertions(+), 18 deletions(-) diff --git a/superset/security.py b/superset/security.py index 3a766d4e1..35545f22e 100644 --- a/superset/security.py +++ b/superset/security.py @@ -106,10 +106,8 @@ class SupersetSecurityManager(SecurityManager): return self._has_view_access(user, permission_name, view_name) def all_datasource_access(self): - return self.can_access('all_datasource_access', 'all_datasource_access') - - def all_database_access(self): - return self.can_access('all_database_access', 'all_database_access') + return self.can_access( + 'all_datasource_access', 'all_datasource_access') def database_access(self, database): return ( @@ -411,12 +409,8 @@ class SupersetSecurityManager(SecurityManager): .values(perm=target.get_perm()), ) - permission_name = 'datasource_access' - from superset.models.core import Database - if mapper.class_ == Database: - permission_name = 'database_access' - # add to view menu if not already exists + permission_name = 'datasource_access' view_menu_name = target.get_perm() permission = self.find_permission(permission_name) view_menu = self.find_view_menu(view_menu_name) diff --git a/superset/views/core.py b/superset/views/core.py index 61fde62c2..becc1b138 100755 --- a/superset/views/core.py +++ b/superset/views/core.py @@ -141,14 +141,6 @@ def check_slice_perms(self, slice_id): security_manager.assert_datasource_permission(viz_obj.datasource) -class DatabaseFilter(SupersetFilter): - def apply(self, query, func): # noqa - if security_manager.all_database_access(): - return query - database_perms = self.get_view_menus('database_access') - return query.filter(self.model.perm.in_(database_perms)) - - class SliceFilter(SupersetFilter): def apply(self, query, func): # noqa if security_manager.all_datasource_access(): @@ -311,7 +303,6 @@ class DatabaseView(SupersetModelView, DeleteMixin, YamlExportMixin): # noqa 'allow_multi_schema_metadata_fetch': _('Allow Multi Schema Metadata Fetch'), 'backend': _('Backend'), } - base_filters = [['id', DatabaseFilter, lambda: []]] def pre_add(self, db): self.check_extra(db) From 9856800cee8b5b08d1a4aa85e8b45698805bd4e4 Mon Sep 17 00:00:00 2001 From: Beto Dealmeida Date: Tue, 9 Apr 2019 20:51:42 -0700 Subject: [PATCH 33/38] Do not add tag to non-saved objects (#7266) --- superset/views/tags.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/superset/views/tags.py b/superset/views/tags.py index 3be876912..cffec8748 100644 --- a/superset/views/tags.py +++ b/superset/views/tags.py @@ -77,6 +77,9 @@ class TagView(BaseSupersetView): @expose('/tags///', methods=['GET']) def get(self, object_type, object_id): """List all tags a given object has.""" + if object_id == 0: + return json_success(json.dumps([])) + query = db.session.query(TaggedObject).filter(and_( TaggedObject.object_type == object_type, TaggedObject.object_id == object_id)) @@ -87,6 +90,9 @@ class TagView(BaseSupersetView): @expose('/tags///', methods=['POST']) def post(self, object_type, object_id): """Add new tags to an object.""" + if object_id == 0: + return Response(status=404) + tagged_objects = [] for name in request.get_json(force=True): if ':' in name: From 65cc0e513c4aa7e82e1900639bb840533d820883 Mon Sep 17 00:00:00 2001 From: Kim Truong <47833996+khtruong@users.noreply.github.com> Date: Tue, 9 Apr 2019 20:52:39 -0700 Subject: [PATCH 34/38] chore: update big number package (#7268) --- superset/assets/package-lock.json | 6 +++--- superset/assets/package.json | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/superset/assets/package-lock.json b/superset/assets/package-lock.json index 41ec051ae..82061f79a 100644 --- a/superset/assets/package-lock.json +++ b/superset/assets/package-lock.json @@ -2583,9 +2583,9 @@ } }, "@superset-ui/legacy-preset-chart-big-number": { - "version": "0.10.4", - "resolved": "https://registry.npmjs.org/@superset-ui/legacy-preset-chart-big-number/-/legacy-preset-chart-big-number-0.10.4.tgz", - "integrity": "sha512-APMIWFFDRIkft6aep7c0VvFr9IV1ge1z+jYRF6UBYFNbwVmlxAKP7FN8amAsj/AAWYSoEyUd+PE0rjPED42oxw==", + "version": "0.10.10", + "resolved": "https://registry.npmjs.org/@superset-ui/legacy-preset-chart-big-number/-/legacy-preset-chart-big-number-0.10.10.tgz", + "integrity": "sha512-p3a3njwHPe1Ya6Cw6undCIVNEgS6jgG1SBiA0S+ILFFmYbKkOhaBRQGJVx+GqwlYeyQhhDOWm4hu646Q6k2yAA==", "requires": { "@data-ui/xy-chart": "^0.0.75", "d3-color": "^1.2.3", diff --git a/superset/assets/package.json b/superset/assets/package.json index 170b75448..d20452eec 100644 --- a/superset/assets/package.json +++ b/superset/assets/package.json @@ -75,7 +75,7 @@ "@superset-ui/legacy-plugin-chart-treemap": "^0.10.0", "@superset-ui/legacy-plugin-chart-word-cloud": "^0.10.0", "@superset-ui/legacy-plugin-chart-world-map": "^0.10.0", - "@superset-ui/legacy-preset-chart-big-number": "^0.10.4", + "@superset-ui/legacy-preset-chart-big-number": "^0.10.10", "@superset-ui/legacy-preset-chart-nvd3": "^0.10.9", "@superset-ui/number-format": "^0.11.0", "@superset-ui/time-format": "^0.11.2", From 0f1702b88e69b4f708a967482308cd0899b9436c Mon Sep 17 00:00:00 2001 From: Kim Truong <47833996+khtruong@users.noreply.github.com> Date: Fri, 12 Apr 2019 16:43:28 -0700 Subject: [PATCH 35/38] chore: update legacy packages (#7293) --- superset/assets/package-lock.json | 152 +++++++++++++++--------------- superset/assets/package.json | 48 +++++----- 2 files changed, 99 insertions(+), 101 deletions(-) diff --git a/superset/assets/package-lock.json b/superset/assets/package-lock.json index 82061f79a..6c45bf5cb 100644 --- a/superset/assets/package-lock.json +++ b/superset/assets/package-lock.json @@ -2270,9 +2270,9 @@ "integrity": "sha512-o7I3tFRphK5KnOJPANTWbgB2LZCGRKUTQX667lsBEKpGFRwz4fbGHZlEIHx6o9kb1PEhc44XJ94hs7mMGr1FDg==" }, "@superset-ui/legacy-plugin-chart-calendar": { - "version": "0.10.0", - "resolved": "https://registry.npmjs.org/@superset-ui/legacy-plugin-chart-calendar/-/legacy-plugin-chart-calendar-0.10.0.tgz", - "integrity": "sha512-nSCAiXH5Q5TO+dc3RDdLQyjn4EF6iFB61KqCwhljr6xlsJOb0kZMXznyURI3D9PCzSZWudvKDPz3i3PZ+by72w==", + "version": "0.10.11", + "resolved": "https://registry.npmjs.org/@superset-ui/legacy-plugin-chart-calendar/-/legacy-plugin-chart-calendar-0.10.11.tgz", + "integrity": "sha512-vAWtITgrPZ+Ploq+8Ye8BjXU4+Z9BvuBukei8kF8uAhrPGOxnnWEkwkdhs5H/wJeTW8Rn6rszyNW1FS2m7IJRA==", "requires": { "d3-array": "^2.0.3", "d3-selection": "^1.4.0", @@ -2292,18 +2292,18 @@ } }, "@superset-ui/legacy-plugin-chart-chord": { - "version": "0.10.0", - "resolved": "https://registry.npmjs.org/@superset-ui/legacy-plugin-chart-chord/-/legacy-plugin-chart-chord-0.10.0.tgz", - "integrity": "sha512-uwFDZq+n0y8YR/LkJAvWVu2TQOK3SdKO/V9Rkf3GMym0H2isVvFo+TR7tbiQtApVW+RwaaRGjptWJwUyGp8PyQ==", + "version": "0.10.11", + "resolved": "https://registry.npmjs.org/@superset-ui/legacy-plugin-chart-chord/-/legacy-plugin-chart-chord-0.10.11.tgz", + "integrity": "sha512-FxpVoDPh8ViYAKmXreCumsKdOZHK9fVgFKyOJVbhxbd+3xv9/KzTCrWi8Ck42CeemLE/mKPJh9wPKE22wpwvpg==", "requires": { "d3": "^3.5.17", "prop-types": "^15.6.2" } }, "@superset-ui/legacy-plugin-chart-country-map": { - "version": "0.10.0", - "resolved": "https://registry.npmjs.org/@superset-ui/legacy-plugin-chart-country-map/-/legacy-plugin-chart-country-map-0.10.0.tgz", - "integrity": "sha512-OziUgugtA/Ofl+XtJ5JqWyvTGjP+IlBzLkaEQtEffB0Ef+qGXGzreFtIxmNKdRhX+P55lEPuKkxv6ZGMVssXRQ==", + "version": "0.10.11", + "resolved": "https://registry.npmjs.org/@superset-ui/legacy-plugin-chart-country-map/-/legacy-plugin-chart-country-map-0.10.11.tgz", + "integrity": "sha512-ecui+gJyPFXHPkRaZi7iJ9phSnV0PTuQmGoMnzsJqnR+aFsnDSZEqPHKVwr9YpFSMSO8FtZVaQosT0IFd0g0mw==", "requires": { "d3": "^3.5.17", "d3-array": "^2.0.3", @@ -2318,27 +2318,27 @@ } }, "@superset-ui/legacy-plugin-chart-event-flow": { - "version": "0.10.0", - "resolved": "https://registry.npmjs.org/@superset-ui/legacy-plugin-chart-event-flow/-/legacy-plugin-chart-event-flow-0.10.0.tgz", - "integrity": "sha512-bi2It5N56A/3WLlUnCNAD7eD7wZtQN34Lf3kTRDeDepzsBpxKAbxdUCLnsMHffM7xSK3l6U0lySJa8yY3AYQzg==", + "version": "0.10.11", + "resolved": "https://registry.npmjs.org/@superset-ui/legacy-plugin-chart-event-flow/-/legacy-plugin-chart-event-flow-0.10.11.tgz", + "integrity": "sha512-ivtXnkkt78q9HSz9P8Zs4iwzyol7Iz1K0mYHYCfgw63ruvxhLTi2ggZ31p2N0h19eE9sobjYEhYPaPR527yV3A==", "requires": { "@data-ui/event-flow": "^0.0.54", "prop-types": "^15.6.2" } }, "@superset-ui/legacy-plugin-chart-force-directed": { - "version": "0.10.0", - "resolved": "https://registry.npmjs.org/@superset-ui/legacy-plugin-chart-force-directed/-/legacy-plugin-chart-force-directed-0.10.0.tgz", - "integrity": "sha512-d3HmpFurgS3KxBvg0hDpt0xRXbWr+i5/bz10jeoodDhXurqE5fNpybVNAFPAGZA6i6RliXFz2Wj+SyJT5rQVKw==", + "version": "0.10.11", + "resolved": "https://registry.npmjs.org/@superset-ui/legacy-plugin-chart-force-directed/-/legacy-plugin-chart-force-directed-0.10.11.tgz", + "integrity": "sha512-lMhYe90O5pL6Wl0+lGriN+77mE+EhvS5UKBItpUJs0455kWk9auEu1ItROwUSG9l3zDGWQR+gBcKPmhpy6fzBw==", "requires": { "d3": "^3.5.17", "prop-types": "^15.6.2" } }, "@superset-ui/legacy-plugin-chart-heatmap": { - "version": "0.10.0", - "resolved": "https://registry.npmjs.org/@superset-ui/legacy-plugin-chart-heatmap/-/legacy-plugin-chart-heatmap-0.10.0.tgz", - "integrity": "sha512-EWnv3iavZ69LSl0VWglx/ewaQVWltOuQRE87tPyqKa0a2pRXcID8LZu652ooMYYlVk14Zx4a2z/Sf/A71X2Txg==", + "version": "0.10.11", + "resolved": "https://registry.npmjs.org/@superset-ui/legacy-plugin-chart-heatmap/-/legacy-plugin-chart-heatmap-0.10.11.tgz", + "integrity": "sha512-VPFRddf7I/ODXUjXosqusWTzV+hVNQYzvQ4/+bhqoBiRyC7hVD9bUGH8q/FIj7/z+HdBuls+EcOM5JvRPB3hpw==", "requires": { "d3": "^3.5.17", "d3-svg-legend": "^1.x", @@ -2347,9 +2347,9 @@ } }, "@superset-ui/legacy-plugin-chart-histogram": { - "version": "0.10.1", - "resolved": "https://registry.npmjs.org/@superset-ui/legacy-plugin-chart-histogram/-/legacy-plugin-chart-histogram-0.10.1.tgz", - "integrity": "sha512-8Vo5HXQO9++CU3GZB21AB6C8cFKSTAs1hMRm0hkM8yuuVCDo/za0RbqeuMf4E8bdGxsc04iguSNGiR+2urac6w==", + "version": "0.10.11", + "resolved": "https://registry.npmjs.org/@superset-ui/legacy-plugin-chart-histogram/-/legacy-plugin-chart-histogram-0.10.11.tgz", + "integrity": "sha512-VyU/v2xXRmdai3+8f6wrh4o/BzEw1wnQ+mXNOvXs0Q4fCrmVn7V9kLoWPKc0ZaRNBB6xQBLLTgRiKE6dAB0hzA==", "requires": { "@data-ui/histogram": "^0.0.77", "@data-ui/theme": "^0.0.77", @@ -2393,9 +2393,9 @@ } }, "@superset-ui/legacy-plugin-chart-horizon": { - "version": "0.10.0", - "resolved": "https://registry.npmjs.org/@superset-ui/legacy-plugin-chart-horizon/-/legacy-plugin-chart-horizon-0.10.0.tgz", - "integrity": "sha512-2MIHoJDtarfFRgxPA8z/xT77k+gquN9NdMpHeyp+ERvyaz5mUevgGnTkjzBA6fluoXbVYIBiSOrFfzMIXeow5g==", + "version": "0.10.11", + "resolved": "https://registry.npmjs.org/@superset-ui/legacy-plugin-chart-horizon/-/legacy-plugin-chart-horizon-0.10.11.tgz", + "integrity": "sha512-A1pjrnOUSLOZAx2uHGxl1zOo4QLYCURYgObnU6Eqcm4AERiuZqZNlktnAqNba5eQNOW0BUqOcEpwDW3ETjiv6Q==", "requires": { "d3-array": "^2.0.3", "d3-scale": "^2.2.2", @@ -2430,9 +2430,9 @@ } }, "@superset-ui/legacy-plugin-chart-iframe": { - "version": "0.10.0", - "resolved": "https://registry.npmjs.org/@superset-ui/legacy-plugin-chart-iframe/-/legacy-plugin-chart-iframe-0.10.0.tgz", - "integrity": "sha512-XbsejMX4yaEsNgOBWQ/UX53Rluep73XkXOxI1NmA8xAdNX/KhsgwEQjWQOPn/boH4suY7Tr86YhOroSl71T1nw==", + "version": "0.10.11", + "resolved": "https://registry.npmjs.org/@superset-ui/legacy-plugin-chart-iframe/-/legacy-plugin-chart-iframe-0.10.11.tgz", + "integrity": "sha512-bOfRIFNoHQnnrL7bwTFE6dTrnm2u8zjYrs3Sv7dAm24iW08W5+tbniaD1bCY3zgrPnJbMiIH9T3LNee4Ra7phQ==", "requires": { "mustache": "^3.0.1", "prop-types": "^15.6.2" @@ -2446,9 +2446,9 @@ } }, "@superset-ui/legacy-plugin-chart-map-box": { - "version": "0.10.0", - "resolved": "https://registry.npmjs.org/@superset-ui/legacy-plugin-chart-map-box/-/legacy-plugin-chart-map-box-0.10.0.tgz", - "integrity": "sha512-+fAw7LAVroB70RB+GSSSvPhHCMxzIpNuSeCo6L2xDVZutf63NRHjXV843gfUd1aRE5oeGeKEBat38OAE0aMR4Q==", + "version": "0.10.11", + "resolved": "https://registry.npmjs.org/@superset-ui/legacy-plugin-chart-map-box/-/legacy-plugin-chart-map-box-0.10.11.tgz", + "integrity": "sha512-FbQzN13blau2RXrCzqL89wZcvG0VCjOuubA2JH5Zaluah+u1UQsxECe3lmv/2GaCMaQbtTJv5CTXOGjxeylc8w==", "requires": { "immutable": "^3.8.2", "mapbox-gl": "^0.53.0", @@ -2459,17 +2459,17 @@ } }, "@superset-ui/legacy-plugin-chart-markup": { - "version": "0.10.0", - "resolved": "https://registry.npmjs.org/@superset-ui/legacy-plugin-chart-markup/-/legacy-plugin-chart-markup-0.10.0.tgz", - "integrity": "sha512-7WNKd6mCAwFkXhaTKZCsL5HsayxScSuBDmIYcxnSKmxdXz9nOu0L/Yi58JlUu+x9soMQeFZaxUTDjoNKyGfG/Q==", + "version": "0.10.11", + "resolved": "https://registry.npmjs.org/@superset-ui/legacy-plugin-chart-markup/-/legacy-plugin-chart-markup-0.10.11.tgz", + "integrity": "sha512-vNrv6+lax1yG3tGMv0PNQkwGlEIpcFDH4py3cOLgU49EtgGMJFAb0PjrWAO7ckZx//ADMbRvSO/uxJd01FF7Eg==", "requires": { "prop-types": "^15.6.2" } }, "@superset-ui/legacy-plugin-chart-paired-t-test": { - "version": "0.10.0", - "resolved": "https://registry.npmjs.org/@superset-ui/legacy-plugin-chart-paired-t-test/-/legacy-plugin-chart-paired-t-test-0.10.0.tgz", - "integrity": "sha512-tPXFE/LAHj2w4/6RnlBI33JjeChUa3y87bGhHEmJgwxPrkwPUizIDzqOyruYw/h2ZD4nta8yU354sUicrYcUYg==", + "version": "0.10.11", + "resolved": "https://registry.npmjs.org/@superset-ui/legacy-plugin-chart-paired-t-test/-/legacy-plugin-chart-paired-t-test-0.10.11.tgz", + "integrity": "sha512-uPPguZLcpRkcS7XkkJd0z82az2sVywZSEBDzB+ZD5sD9zi9nglD1MSB9gC+B5cyQ0wai4IxhtjhsiV6KtWJUMg==", "requires": { "distributions": "^1.0.0", "prop-types": "^15.6.2", @@ -2477,18 +2477,18 @@ } }, "@superset-ui/legacy-plugin-chart-parallel-coordinates": { - "version": "0.10.0", - "resolved": "https://registry.npmjs.org/@superset-ui/legacy-plugin-chart-parallel-coordinates/-/legacy-plugin-chart-parallel-coordinates-0.10.0.tgz", - "integrity": "sha512-XGV61ynoxB16CLwZPeQXJ04UVmjw8dKetW5DW9gQs/WobrMO3hYAVzAux2rUlmubVM2jtCls43+2KDtg0clhFA==", + "version": "0.10.11", + "resolved": "https://registry.npmjs.org/@superset-ui/legacy-plugin-chart-parallel-coordinates/-/legacy-plugin-chart-parallel-coordinates-0.10.11.tgz", + "integrity": "sha512-pyoWa9w3Bm2jR9vBZveU+Q15aLD9ofM/6E7lj1nRl1fCqDH1mRjLuv7JWsDn6b6K7e1i9JrMySGZE443/v4tig==", "requires": { "d3": "^3.5.17", "prop-types": "^15.6.2" } }, "@superset-ui/legacy-plugin-chart-partition": { - "version": "0.10.0", - "resolved": "https://registry.npmjs.org/@superset-ui/legacy-plugin-chart-partition/-/legacy-plugin-chart-partition-0.10.0.tgz", - "integrity": "sha512-UZcEGqSJ2APF+fOGyUJ/RR0Y5fKsx79ihGnm4IO/N62vt9/Nd/1BCH0C6oUp9CmLwZ3JLwPFFRRWu948vPzkPQ==", + "version": "0.10.11", + "resolved": "https://registry.npmjs.org/@superset-ui/legacy-plugin-chart-partition/-/legacy-plugin-chart-partition-0.10.11.tgz", + "integrity": "sha512-dDflVZNCGwfKBLSVcysE6x0MBeOz1IrXo89veID1QuA398spKPXJyLFRl+qPox0r4+L7qEjHz239NmEohzlT8g==", "requires": { "d3": "^3.5.17", "d3-hierarchy": "^1.1.8", @@ -2496,9 +2496,9 @@ } }, "@superset-ui/legacy-plugin-chart-pivot-table": { - "version": "0.10.3", - "resolved": "https://registry.npmjs.org/@superset-ui/legacy-plugin-chart-pivot-table/-/legacy-plugin-chart-pivot-table-0.10.3.tgz", - "integrity": "sha512-zt6UVRu1QsPQqo3dJGT0O+mhM6hWKbg5KtrTZH/Z9WNdXrMVm1VARcAyjG2Yt7WZ7dCAPS+nJJ9V779AP/d5dw==", + "version": "0.10.11", + "resolved": "https://registry.npmjs.org/@superset-ui/legacy-plugin-chart-pivot-table/-/legacy-plugin-chart-pivot-table-0.10.11.tgz", + "integrity": "sha512-qwPY/p7NQ8ax7qMYL1bkQykA0j8W+Hbfl+fMWM0VBnbFRh8gWrja0n5y2KQv0REqDE58LwrbDJJ9bYz5JUEHGw==", "requires": { "d3": "^3.5.17", "datatables.net-bs": "^1.10.15", @@ -2506,9 +2506,9 @@ } }, "@superset-ui/legacy-plugin-chart-rose": { - "version": "0.10.0", - "resolved": "https://registry.npmjs.org/@superset-ui/legacy-plugin-chart-rose/-/legacy-plugin-chart-rose-0.10.0.tgz", - "integrity": "sha512-yEY0UoJBpfuRTljZYmpvipspEG6M2nVq65XERqQiH4DbcAui5ai8q19tVVbNsySK6m8zYlGXZVwbuYulItjhOw==", + "version": "0.10.11", + "resolved": "https://registry.npmjs.org/@superset-ui/legacy-plugin-chart-rose/-/legacy-plugin-chart-rose-0.10.11.tgz", + "integrity": "sha512-/OGFszFFr64dFZsig+GyGXJOQkXLMq1/fZ3WqwAn6yfucDWtGhLMRILUJQs9p5fP18fRTsr6ndZ4LJmWGRY+/w==", "requires": { "d3": "^3.5.17", "nvd3": "1.8.6", @@ -2516,9 +2516,9 @@ } }, "@superset-ui/legacy-plugin-chart-sankey": { - "version": "0.10.0", - "resolved": "https://registry.npmjs.org/@superset-ui/legacy-plugin-chart-sankey/-/legacy-plugin-chart-sankey-0.10.0.tgz", - "integrity": "sha512-0wranvDMyvyhq+3LhGw90uor2KA/3KwB/yaRB4CDAGfgcbz9kkNEkrAt+BwuzwQ3xaKCuuPkBDcR5pcNpCEpGg==", + "version": "0.10.11", + "resolved": "https://registry.npmjs.org/@superset-ui/legacy-plugin-chart-sankey/-/legacy-plugin-chart-sankey-0.10.11.tgz", + "integrity": "sha512-dhmL6FzSODkYDuXJ9N1YnWUnDIwWn0NldQNXtNcHUjyZqVzS5cm/J1CX3KCd2F/FOU3dNfq/izm4UCpACNydYw==", "requires": { "d3": "^3.5.17", "d3-sankey": "^0.4.2", @@ -2526,18 +2526,18 @@ } }, "@superset-ui/legacy-plugin-chart-sunburst": { - "version": "0.10.0", - "resolved": "https://registry.npmjs.org/@superset-ui/legacy-plugin-chart-sunburst/-/legacy-plugin-chart-sunburst-0.10.0.tgz", - "integrity": "sha512-6K8H1WK6+7uGN0oFMIVAI3JxnoQkye1HBXIRc5yQFMeDpX2N04+I6JKZBM8hOEELWmZU8OOnX6hY3qQLHR80pw==", + "version": "0.10.11", + "resolved": "https://registry.npmjs.org/@superset-ui/legacy-plugin-chart-sunburst/-/legacy-plugin-chart-sunburst-0.10.11.tgz", + "integrity": "sha512-YtBse4TtrtRM6fBK0SnEStwoJkmesxps6Cx4m42CN6CfXcxEoKJZ3t7iEvVNa0IsG9PRr7A67SZy42DvmjbwWA==", "requires": { "d3": "^3.5.17", "prop-types": "^15.6.2" } }, "@superset-ui/legacy-plugin-chart-table": { - "version": "0.10.0", - "resolved": "https://registry.npmjs.org/@superset-ui/legacy-plugin-chart-table/-/legacy-plugin-chart-table-0.10.0.tgz", - "integrity": "sha512-6bXJM2JeMABj3XO8gD+Bv0Fq/YViQR0vwcm1jKkB7hpPRLRaV6NwspIG081QKs0MeNFDGXJnKfrSM271MrwQbQ==", + "version": "0.10.11", + "resolved": "https://registry.npmjs.org/@superset-ui/legacy-plugin-chart-table/-/legacy-plugin-chart-table-0.10.11.tgz", + "integrity": "sha512-6otRNz+3Jxa2kDTuIpsom2E67ZlnUTcS1ETX0P1rcgWrwSmrwTnNwSiH+bRR82YRr9xZDH6hxoJigyytWwDPlg==", "requires": { "d3": "^3.5.17", "datatables.net-bs": "^1.10.15", @@ -2546,9 +2546,9 @@ } }, "@superset-ui/legacy-plugin-chart-treemap": { - "version": "0.10.0", - "resolved": "https://registry.npmjs.org/@superset-ui/legacy-plugin-chart-treemap/-/legacy-plugin-chart-treemap-0.10.0.tgz", - "integrity": "sha512-MKw139JP9DZ480fHP9VoMKZkNiPN3oK9zSqPuFYY1QMeTlN3h5AchuxVVwC/kVuwT1ANzM0RyKWIXIloI7Fpww==", + "version": "0.10.11", + "resolved": "https://registry.npmjs.org/@superset-ui/legacy-plugin-chart-treemap/-/legacy-plugin-chart-treemap-0.10.11.tgz", + "integrity": "sha512-fw229yKIHvhUis7XmoNPDoPHhVaCJd7FfYM6Cepq0cp5JgubOWYHRZy4qUxZVETuBE1u+IXTt28SY4KGvmR+xA==", "requires": { "d3-hierarchy": "^1.1.8", "d3-selection": "^1.4.0", @@ -2563,9 +2563,9 @@ } }, "@superset-ui/legacy-plugin-chart-word-cloud": { - "version": "0.10.0", - "resolved": "https://registry.npmjs.org/@superset-ui/legacy-plugin-chart-word-cloud/-/legacy-plugin-chart-word-cloud-0.10.0.tgz", - "integrity": "sha512-Ffm0XaODPpKTMCUdWvSXqH6zL0V3vB2vo3vZPaq2lzBIzPl4Jd81Nfyv32uMH2OmWeChGJ8k0SnmRQyosePttQ==", + "version": "0.10.11", + "resolved": "https://registry.npmjs.org/@superset-ui/legacy-plugin-chart-word-cloud/-/legacy-plugin-chart-word-cloud-0.10.11.tgz", + "integrity": "sha512-ZJZqEhmu6K+sl31rqhKdR7BoEXxpuS69NJkAdYyQu0pNE4noIwHMXSfn/TIxD3x4Hkjn4tJE/A4Ro/FiPD+c5Q==", "requires": { "d3": "^3.5.17", "d3-cloud": "^1.2.1", @@ -2573,9 +2573,9 @@ } }, "@superset-ui/legacy-plugin-chart-world-map": { - "version": "0.10.0", - "resolved": "https://registry.npmjs.org/@superset-ui/legacy-plugin-chart-world-map/-/legacy-plugin-chart-world-map-0.10.0.tgz", - "integrity": "sha512-0auW1nljyR6elgSaPHuY1kJVprLFh/JMFwaCn6W56TRaIe/hszY7sqimw5vkbdT93aeZCAZC1IMkOIGTv5wTgg==", + "version": "0.10.11", + "resolved": "https://registry.npmjs.org/@superset-ui/legacy-plugin-chart-world-map/-/legacy-plugin-chart-world-map-0.10.11.tgz", + "integrity": "sha512-VHtEl7YrcIJjxGaXRGiXpC8k/MhfeCId61/JwEr8kiQjH9dPZ+mmH+dry6DE3OYgoqEXJUNJduk+gHTLzBRSLw==", "requires": { "d3": "^3.5.17", "datamaps": "^0.5.8", @@ -2583,9 +2583,9 @@ } }, "@superset-ui/legacy-preset-chart-big-number": { - "version": "0.10.10", - "resolved": "https://registry.npmjs.org/@superset-ui/legacy-preset-chart-big-number/-/legacy-preset-chart-big-number-0.10.10.tgz", - "integrity": "sha512-p3a3njwHPe1Ya6Cw6undCIVNEgS6jgG1SBiA0S+ILFFmYbKkOhaBRQGJVx+GqwlYeyQhhDOWm4hu646Q6k2yAA==", + "version": "0.10.11", + "resolved": "https://registry.npmjs.org/@superset-ui/legacy-preset-chart-big-number/-/legacy-preset-chart-big-number-0.10.11.tgz", + "integrity": "sha512-rNZWvY/jYmDgHaY+VZbNbUMmJXtiwYIhZAH6X5gOa6GRF2Cu1tS96yj5DdwdeH2IJk47jGzcSaRLxRHkcyUeKw==", "requires": { "@data-ui/xy-chart": "^0.0.75", "d3-color": "^1.2.3", @@ -2594,9 +2594,9 @@ } }, "@superset-ui/legacy-preset-chart-nvd3": { - "version": "0.10.9", - "resolved": "https://registry.npmjs.org/@superset-ui/legacy-preset-chart-nvd3/-/legacy-preset-chart-nvd3-0.10.9.tgz", - "integrity": "sha512-2XLyPqDXFhDYGCBeEueGabF9Leh+X5bzR/AaXP3RQdfpwFSty0fo9IIAbJuq/3+0re/WuGW3xdN5h/No/n66qQ==", + "version": "0.10.11", + "resolved": "https://registry.npmjs.org/@superset-ui/legacy-preset-chart-nvd3/-/legacy-preset-chart-nvd3-0.10.11.tgz", + "integrity": "sha512-xJesg+e1g8Og3u2A+MReu8kMYnkcrfLd/FapaLhFV3hEiU2u8UeuaBfKy2JUQXzsgr99E24FPg0Qvpg/zN9/YA==", "requires": { "@data-ui/xy-chart": "^0.0.75", "d3": "^3.5.17", @@ -16738,13 +16738,11 @@ } }, "react-split-pane": { - "version": "0.1.85", - "resolved": "https://registry.npmjs.org/react-split-pane/-/react-split-pane-0.1.85.tgz", - "integrity": "sha512-3GhaYs6+eVNrewgN4eQKJoNMQ4pcegNMTMhR5bO/NFO91K6/98qdD1sCuWPpsefCjzxNTjkvVYWQC0bMaC45mA==", + "version": "0.1.87", + "resolved": "https://registry.npmjs.org/react-split-pane/-/react-split-pane-0.1.87.tgz", + "integrity": "sha512-F22jqWyKB1WximT0U5HKdSuB9tmJGjjP+WUyveHxJJys3ANsljj163kCdsI6M3gdfyCVC+B2rq8sc5m2Ko02RA==", "requires": { "prop-types": "^15.5.10", - "react": "^16.6.3", - "react-dom": "^16.6.3", "react-lifecycles-compat": "^3.0.4", "react-style-proptype": "^3.0.0" } diff --git a/superset/assets/package.json b/superset/assets/package.json index d20452eec..baeeb3fdc 100644 --- a/superset/assets/package.json +++ b/superset/assets/package.json @@ -53,30 +53,30 @@ "@superset-ui/connection": "^0.11.0", "@superset-ui/core": "^0.11.0", "@superset-ui/dimension": "^0.11.0", - "@superset-ui/legacy-plugin-chart-calendar": "^0.10.0", - "@superset-ui/legacy-plugin-chart-chord": "^0.10.0", - "@superset-ui/legacy-plugin-chart-country-map": "^0.10.0", - "@superset-ui/legacy-plugin-chart-event-flow": "^0.10.0", - "@superset-ui/legacy-plugin-chart-force-directed": "^0.10.0", - "@superset-ui/legacy-plugin-chart-heatmap": "^0.10.0", - "@superset-ui/legacy-plugin-chart-histogram": "^0.10.1", - "@superset-ui/legacy-plugin-chart-horizon": "^0.10.0", - "@superset-ui/legacy-plugin-chart-iframe": "^0.10.0", - "@superset-ui/legacy-plugin-chart-map-box": "^0.10.0", - "@superset-ui/legacy-plugin-chart-markup": "^0.10.0", - "@superset-ui/legacy-plugin-chart-paired-t-test": "^0.10.0", - "@superset-ui/legacy-plugin-chart-parallel-coordinates": "^0.10.0", - "@superset-ui/legacy-plugin-chart-partition": "^0.10.0", - "@superset-ui/legacy-plugin-chart-pivot-table": "^0.10.3", - "@superset-ui/legacy-plugin-chart-rose": "^0.10.0", - "@superset-ui/legacy-plugin-chart-sankey": "^0.10.0", - "@superset-ui/legacy-plugin-chart-sunburst": "^0.10.0", - "@superset-ui/legacy-plugin-chart-table": "^0.10.0", - "@superset-ui/legacy-plugin-chart-treemap": "^0.10.0", - "@superset-ui/legacy-plugin-chart-word-cloud": "^0.10.0", - "@superset-ui/legacy-plugin-chart-world-map": "^0.10.0", - "@superset-ui/legacy-preset-chart-big-number": "^0.10.10", - "@superset-ui/legacy-preset-chart-nvd3": "^0.10.9", + "@superset-ui/legacy-plugin-chart-calendar": "^0.10.11", + "@superset-ui/legacy-plugin-chart-chord": "^0.10.11", + "@superset-ui/legacy-plugin-chart-country-map": "^0.10.11", + "@superset-ui/legacy-plugin-chart-event-flow": "^0.10.11", + "@superset-ui/legacy-plugin-chart-force-directed": "^0.10.11", + "@superset-ui/legacy-plugin-chart-heatmap": "^0.10.11", + "@superset-ui/legacy-plugin-chart-histogram": "^0.10.11", + "@superset-ui/legacy-plugin-chart-horizon": "^0.10.11", + "@superset-ui/legacy-plugin-chart-iframe": "^0.10.11", + "@superset-ui/legacy-plugin-chart-map-box": "^0.10.11", + "@superset-ui/legacy-plugin-chart-markup": "^0.10.11", + "@superset-ui/legacy-plugin-chart-paired-t-test": "^0.10.11", + "@superset-ui/legacy-plugin-chart-parallel-coordinates": "^0.10.11", + "@superset-ui/legacy-plugin-chart-partition": "^0.10.11", + "@superset-ui/legacy-plugin-chart-pivot-table": "^0.10.11", + "@superset-ui/legacy-plugin-chart-rose": "^0.10.11", + "@superset-ui/legacy-plugin-chart-sankey": "^0.10.11", + "@superset-ui/legacy-plugin-chart-sunburst": "^0.10.11", + "@superset-ui/legacy-plugin-chart-table": "^0.10.11", + "@superset-ui/legacy-plugin-chart-treemap": "^0.10.11", + "@superset-ui/legacy-plugin-chart-word-cloud": "^0.10.11", + "@superset-ui/legacy-plugin-chart-world-map": "^0.10.11", + "@superset-ui/legacy-preset-chart-big-number": "^0.10.11", + "@superset-ui/legacy-preset-chart-nvd3": "^0.10.11", "@superset-ui/number-format": "^0.11.0", "@superset-ui/time-format": "^0.11.2", "@superset-ui/translation": "^0.11.0", From 5dab983fd8be6463e58a699e59177846c774c816 Mon Sep 17 00:00:00 2001 From: Kim Truong <47833996+khtruong@users.noreply.github.com> Date: Tue, 16 Apr 2019 10:33:12 -0700 Subject: [PATCH 36/38] fix: time series table (#7302) * fix: time series table * fix: add default value for label * fix: use prop values if defined * fix: revert CollectionControl changes --- .../controls/TimeSeriesColumnControl.jsx | 43 ++++++++++++++++++- 1 file changed, 42 insertions(+), 1 deletion(-) diff --git a/superset/assets/src/explore/components/controls/TimeSeriesColumnControl.jsx b/superset/assets/src/explore/components/controls/TimeSeriesColumnControl.jsx index 24ec40102..145f80f75 100644 --- a/superset/assets/src/explore/components/controls/TimeSeriesColumnControl.jsx +++ b/superset/assets/src/explore/components/controls/TimeSeriesColumnControl.jsx @@ -22,16 +22,43 @@ import { Row, Col, FormControl, OverlayTrigger, Popover, } from 'react-bootstrap'; import Select from 'react-select'; +import { t } from '@superset-ui/translation'; import InfoTooltipWithTrigger from '../../../components/InfoTooltipWithTrigger'; import BoundsControl from './BoundsControl'; import CheckboxControl from './CheckboxControl'; const propTypes = { + label: PropTypes.string, + tooltip: PropTypes.string, + colType: PropTypes.string, + width: PropTypes.string, + height: PropTypes.string, + timeLag: PropTypes.string, + timeRatio: PropTypes.string, + comparisonType: PropTypes.string, + showYAxis: PropTypes.bool, + yAxisBounds: PropTypes.array, + bounds: PropTypes.array, + d3format: PropTypes.string, + dateFormat: PropTypes.string, onChange: PropTypes.func, }; const defaultProps = { + label: t('Time Series Columns'), + tooltip: '', + colType: '', + width: '', + height: '', + timeLag: '', + timeRatio: '', + comparisonType: '', + showYAxis: false, + yAxisBounds: [null, null], + bounds: [null, null], + d3format: '', + dateFormat: '', onChange: () => {}, }; @@ -52,7 +79,21 @@ const colTypeOptions = [ export default class TimeSeriesColumnControl extends React.Component { constructor(props) { super(props); - const state = { ...props }; + const state = { + label: this.props.label, + tooltip: this.props.tooltip, + colType: this.props.colType, + width: this.props.width, + height: this.props.height, + timeLag: this.props.timeLag, + timeRatio: this.props.timeRatio, + comparisonType: this.props.comparisonType, + showYAxis: this.props.showYAxis, + yAxisBounds: this.props.yAxisBounds, + bounds: this.props.bounds, + d3format: this.props.d3format, + dateFormat: this.props.dateFormat, + }; delete state.onChange; this.state = state; this.onChange = this.onChange.bind(this); From 81a1e5322b36a7d3006b6dd5b6ab33b0ac1fc913 Mon Sep 17 00:00:00 2001 From: Kim Truong <47833996+khtruong@users.noreply.github.com> Date: Wed, 17 Apr 2019 15:36:59 -0700 Subject: [PATCH 37/38] fix: propagate color mapping from dashboard to charts (#7289) * fix: propagate color map from dashboard to chart * fix: handle cache scenario * fix: give default values --- superset/assets/backendSync.json | 5 ++ .../src/dashboard/components/Dashboard.jsx | 2 + .../assets/src/dashboard/containers/Chart.jsx | 3 +- .../charts/getFormDataWithExtraFilters.js | 10 ++++ .../components/controls/ColorMapControl.jsx | 54 +++++++++++++++++++ .../src/explore/components/controls/index.js | 2 + .../assets/src/explore/controlPanels/Area.js | 2 +- .../assets/src/explore/controlPanels/Bar.js | 2 +- .../src/explore/controlPanels/BoxPlot.js | 2 +- .../src/explore/controlPanels/Bubble.js | 2 +- .../assets/src/explore/controlPanels/Chord.js | 2 +- .../src/explore/controlPanels/Compare.js | 2 +- .../src/explore/controlPanels/DeckArc.js | 2 +- .../src/explore/controlPanels/DeckScatter.js | 2 +- .../src/explore/controlPanels/DistBar.js | 2 +- .../src/explore/controlPanels/DualLine.js | 2 +- .../src/explore/controlPanels/Histogram.js | 2 +- .../assets/src/explore/controlPanels/Line.js | 2 +- .../src/explore/controlPanels/LineMulti.js | 2 +- .../src/explore/controlPanels/Partition.js | 2 +- .../assets/src/explore/controlPanels/Pie.js | 2 +- .../assets/src/explore/controlPanels/Rose.js | 2 +- .../src/explore/controlPanels/Sankey.js | 2 +- .../src/explore/controlPanels/Sunburst.js | 2 +- .../src/explore/controlPanels/Treemap.js | 2 +- .../src/explore/controlPanels/WordCloud.js | 2 +- .../src/explore/controlPanels/sections.jsx | 2 +- superset/assets/src/explore/controls.jsx | 10 ++++ 28 files changed, 106 insertions(+), 22 deletions(-) create mode 100644 superset/assets/src/explore/components/controls/ColorMapControl.jsx diff --git a/superset/assets/backendSync.json b/superset/assets/backendSync.json index 68684bdcb..2285f50ba 100644 --- a/superset/assets/backendSync.json +++ b/superset/assets/backendSync.json @@ -3278,6 +3278,11 @@ "renderTrigger": true, "description": "The color scheme for rendering chart" }, + "label_colors": { + "type": "ColorMapControl", + "label": "Color Map", + "default": {} + }, "significance_level": { "type": "TextControl", "label": "Significance Level", diff --git a/superset/assets/src/dashboard/components/Dashboard.jsx b/superset/assets/src/dashboard/components/Dashboard.jsx index 32cc3768c..b26cde6f2 100644 --- a/superset/assets/src/dashboard/components/Dashboard.jsx +++ b/superset/assets/src/dashboard/components/Dashboard.jsx @@ -153,6 +153,8 @@ class Dashboard extends React.PureComponent { chart, dashboardMetadata: this.props.dashboardInfo.metadata, filters: this.props.dashboardState.filters, + colorScheme: this.props.dashboardState.colorScheme, + colorNamespace: this.props.dashboardState.colorNamespace, sliceId: chart.id, }); diff --git a/superset/assets/src/dashboard/containers/Chart.jsx b/superset/assets/src/dashboard/containers/Chart.jsx index c0926db0a..5b14c1bad 100644 --- a/superset/assets/src/dashboard/containers/Chart.jsx +++ b/superset/assets/src/dashboard/containers/Chart.jsx @@ -43,7 +43,7 @@ function mapStateToProps( ) { const { id } = ownProps; const chart = chartQueries[id] || {}; - const { filters, colorScheme } = dashboardState; + const { filters, colorScheme, colorNamespace } = dashboardState; return { chart, @@ -59,6 +59,7 @@ function mapStateToProps( dashboardMetadata: dashboardInfo.metadata, filters, colorScheme, + colorNamespace, sliceId: id, }), editMode: dashboardState.editMode, diff --git a/superset/assets/src/dashboard/util/charts/getFormDataWithExtraFilters.js b/superset/assets/src/dashboard/util/charts/getFormDataWithExtraFilters.js index a928a12b9..5869a095a 100644 --- a/superset/assets/src/dashboard/util/charts/getFormDataWithExtraFilters.js +++ b/superset/assets/src/dashboard/util/charts/getFormDataWithExtraFilters.js @@ -16,6 +16,8 @@ * specific language governing permissions and limitations * under the License. */ +import { isEqual } from 'lodash'; +import { CategoricalColorNamespace } from '@superset-ui/color'; import getEffectiveExtraFilters from './getEffectiveExtraFilters'; // We cache formData objects so that our connected container components don't always trigger @@ -29,14 +31,21 @@ export default function getFormDataWithExtraFilters({ dashboardMetadata, filters, colorScheme, + colorNamespace, sliceId, }) { + // Propagate color mapping to chart + const scale = CategoricalColorNamespace.getScale(colorScheme, colorNamespace); + const labelColors = scale.getColorMap(); + // if dashboard metadata + filters have not changed, use cache if possible if ( (cachedDashboardMetadataByChart[sliceId] || {}) === dashboardMetadata && (cachedFiltersByChart[sliceId] || {}) === filters && (colorScheme == null || cachedFormdataByChart[sliceId].color_scheme === colorScheme) && + cachedFormdataByChart[sliceId].color_namespace === colorNamespace && + isEqual(cachedFormdataByChart[sliceId].label_colors, labelColors) && !!cachedFormdataByChart[sliceId] ) { return cachedFormdataByChart[sliceId]; @@ -45,6 +54,7 @@ export default function getFormDataWithExtraFilters({ const formData = { ...chart.formData, ...(colorScheme && { color_scheme: colorScheme }), + label_colors: labelColors, extra_filters: getEffectiveExtraFilters({ dashboardMetadata, filters, diff --git a/superset/assets/src/explore/components/controls/ColorMapControl.jsx b/superset/assets/src/explore/components/controls/ColorMapControl.jsx new file mode 100644 index 000000000..08cddef1f --- /dev/null +++ b/superset/assets/src/explore/components/controls/ColorMapControl.jsx @@ -0,0 +1,54 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +import PropTypes from 'prop-types'; +import React from 'react'; +import { CategoricalColorNamespace } from '@superset-ui/color'; + +const propTypes = { + onChange: PropTypes.func, + value: PropTypes.object, + colorScheme: PropTypes.string, + colorNamespace: PropTypes.string, +}; + +const defaultProps = { + onChange: () => {}, + value: {}, + colorScheme: undefined, + colorNamespace: undefined, +}; + +export default class ColorMapControl extends React.PureComponent { + constructor(props) { + super(props); + Object.keys(this.props.value).forEach((label) => { + CategoricalColorNamespace.getScale( + this.props.colorScheme, + this.props.colorNamespace, + ).setColor(label, this.props.value[label]); + }); + } + + render() { + return null; + } +} + +ColorMapControl.propTypes = propTypes; +ColorMapControl.defaultProps = defaultProps; diff --git a/superset/assets/src/explore/components/controls/index.js b/superset/assets/src/explore/components/controls/index.js index d1a2f2c27..32a8d449c 100644 --- a/superset/assets/src/explore/components/controls/index.js +++ b/superset/assets/src/explore/components/controls/index.js @@ -20,6 +20,7 @@ import AnnotationLayerControl from './AnnotationLayerControl'; import BoundsControl from './BoundsControl'; import CheckboxControl from './CheckboxControl'; import CollectionControl from './CollectionControl'; +import ColorMapControl from './ColorMapControl'; import ColorPickerControl from './ColorPickerControl'; import ColorSchemeControl from './ColorSchemeControl'; import DatasourceControl from './DatasourceControl'; @@ -45,6 +46,7 @@ const controlMap = { BoundsControl, CheckboxControl, CollectionControl, + ColorMapControl, ColorPickerControl, ColorSchemeControl, DatasourceControl, diff --git a/superset/assets/src/explore/controlPanels/Area.js b/superset/assets/src/explore/controlPanels/Area.js index 73b5994a8..b978cd15e 100644 --- a/superset/assets/src/explore/controlPanels/Area.js +++ b/superset/assets/src/explore/controlPanels/Area.js @@ -30,7 +30,7 @@ export default { controlSetRows: [ ['show_brush', 'show_legend'], ['line_interpolation', 'stacked_style'], - ['color_scheme'], + ['color_scheme', 'label_colors'], ['rich_tooltip', 'show_controls'], ], }, diff --git a/superset/assets/src/explore/controlPanels/Bar.js b/superset/assets/src/explore/controlPanels/Bar.js index c3cbc733e..b21fb4727 100644 --- a/superset/assets/src/explore/controlPanels/Bar.js +++ b/superset/assets/src/explore/controlPanels/Bar.js @@ -28,7 +28,7 @@ export default { label: t('Chart Options'), expanded: true, controlSetRows: [ - ['color_scheme'], + ['color_scheme', 'label_colors'], ['show_brush', 'show_legend', 'show_bar_value'], ['rich_tooltip', 'bar_stacked'], ['line_interpolation', 'show_controls'], diff --git a/superset/assets/src/explore/controlPanels/BoxPlot.js b/superset/assets/src/explore/controlPanels/BoxPlot.js index 142c1d663..1cdf320d9 100644 --- a/superset/assets/src/explore/controlPanels/BoxPlot.js +++ b/superset/assets/src/explore/controlPanels/BoxPlot.js @@ -34,7 +34,7 @@ export default { label: t('Chart Options'), expanded: true, controlSetRows: [ - ['color_scheme'], + ['color_scheme', 'label_colors'], ['whisker_options', 'x_ticks_layout'], ], }, diff --git a/superset/assets/src/explore/controlPanels/Bubble.js b/superset/assets/src/explore/controlPanels/Bubble.js index 638e6d105..891fe29cf 100644 --- a/superset/assets/src/explore/controlPanels/Bubble.js +++ b/superset/assets/src/explore/controlPanels/Bubble.js @@ -38,7 +38,7 @@ export default { label: t('Chart Options'), expanded: true, controlSetRows: [ - ['color_scheme'], + ['color_scheme', 'label_colors'], ['show_legend', null], ], }, diff --git a/superset/assets/src/explore/controlPanels/Chord.js b/superset/assets/src/explore/controlPanels/Chord.js index 18ae6eb67..b932c3f0e 100644 --- a/superset/assets/src/explore/controlPanels/Chord.js +++ b/superset/assets/src/explore/controlPanels/Chord.js @@ -37,7 +37,7 @@ export default { expanded: true, controlSetRows: [ ['y_axis_format', null], - ['color_scheme'], + ['color_scheme', 'label_colors'], ], }, ], diff --git a/superset/assets/src/explore/controlPanels/Compare.js b/superset/assets/src/explore/controlPanels/Compare.js index 091e8db49..3c50834ae 100644 --- a/superset/assets/src/explore/controlPanels/Compare.js +++ b/superset/assets/src/explore/controlPanels/Compare.js @@ -28,7 +28,7 @@ export default { label: t('Chart Options'), expanded: true, controlSetRows: [ - ['color_scheme'], + ['color_scheme', 'label_colors'], ], }, { diff --git a/superset/assets/src/explore/controlPanels/DeckArc.js b/superset/assets/src/explore/controlPanels/DeckArc.js index d54d59dc5..e0bee756f 100644 --- a/superset/assets/src/explore/controlPanels/DeckArc.js +++ b/superset/assets/src/explore/controlPanels/DeckArc.js @@ -42,7 +42,7 @@ export default { label: t('Arc'), controlSetRows: [ ['color_picker', 'target_color_picker'], - ['dimension', 'color_scheme'], + ['dimension', 'color_scheme', 'label_colors'], ['stroke_width', 'legend_position'], ], }, diff --git a/superset/assets/src/explore/controlPanels/DeckScatter.js b/superset/assets/src/explore/controlPanels/DeckScatter.js index 79476228c..8e60029fe 100644 --- a/superset/assets/src/explore/controlPanels/DeckScatter.js +++ b/superset/assets/src/explore/controlPanels/DeckScatter.js @@ -62,7 +62,7 @@ export default { label: t('Point Color'), controlSetRows: [ ['color_picker', 'legend_position'], - ['dimension', 'color_scheme'], + ['dimension', 'color_scheme', 'label_colors'], ], }, { diff --git a/superset/assets/src/explore/controlPanels/DistBar.js b/superset/assets/src/explore/controlPanels/DistBar.js index fff22d688..41b944e1c 100644 --- a/superset/assets/src/explore/controlPanels/DistBar.js +++ b/superset/assets/src/explore/controlPanels/DistBar.js @@ -36,7 +36,7 @@ export default { label: t('Chart Options'), expanded: true, controlSetRows: [ - ['color_scheme'], + ['color_scheme', 'label_colors'], ['show_legend', 'show_bar_value'], ['bar_stacked', 'order_bars'], ['y_axis_format', 'y_axis_label'], diff --git a/superset/assets/src/explore/controlPanels/DualLine.js b/superset/assets/src/explore/controlPanels/DualLine.js index 4792b4a2c..11d930fd3 100644 --- a/superset/assets/src/explore/controlPanels/DualLine.js +++ b/superset/assets/src/explore/controlPanels/DualLine.js @@ -27,7 +27,7 @@ export default { label: t('Chart Options'), expanded: true, controlSetRows: [ - ['color_scheme'], + ['color_scheme', 'label_colors'], ['x_axis_format'], ], }, diff --git a/superset/assets/src/explore/controlPanels/Histogram.js b/superset/assets/src/explore/controlPanels/Histogram.js index 1df41842f..d95ff80a2 100644 --- a/superset/assets/src/explore/controlPanels/Histogram.js +++ b/superset/assets/src/explore/controlPanels/Histogram.js @@ -35,7 +35,7 @@ export default { label: t('Chart Options'), expanded: true, controlSetRows: [ - ['color_scheme'], + ['color_scheme', 'label_colors'], ['link_length'], ['x_axis_label', 'y_axis_label'], ['global_opacity'], diff --git a/superset/assets/src/explore/controlPanels/Line.js b/superset/assets/src/explore/controlPanels/Line.js index 012976a6e..a92427dee 100644 --- a/superset/assets/src/explore/controlPanels/Line.js +++ b/superset/assets/src/explore/controlPanels/Line.js @@ -28,7 +28,7 @@ export default { label: t('Chart Options'), expanded: true, controlSetRows: [ - ['color_scheme'], + ['color_scheme', 'label_colors'], ['show_brush', 'send_time_range', 'show_legend'], ['rich_tooltip', 'show_markers'], ['line_interpolation'], diff --git a/superset/assets/src/explore/controlPanels/LineMulti.js b/superset/assets/src/explore/controlPanels/LineMulti.js index 0966712bd..71c0182ed 100644 --- a/superset/assets/src/explore/controlPanels/LineMulti.js +++ b/superset/assets/src/explore/controlPanels/LineMulti.js @@ -27,7 +27,7 @@ export default { label: t('Chart Options'), expanded: true, controlSetRows: [ - ['color_scheme'], + ['color_scheme', 'label_colors'], ['prefix_metric_with_slice_name', null], ['show_legend', 'show_markers'], ['line_interpolation', null], diff --git a/superset/assets/src/explore/controlPanels/Partition.js b/superset/assets/src/explore/controlPanels/Partition.js index 2bee2180a..a450da2ba 100644 --- a/superset/assets/src/explore/controlPanels/Partition.js +++ b/superset/assets/src/explore/controlPanels/Partition.js @@ -33,7 +33,7 @@ export default { label: t('Chart Options'), expanded: true, controlSetRows: [ - ['color_scheme'], + ['color_scheme', 'label_colors'], ['number_format', 'date_time_format'], ['partition_limit', 'partition_threshold'], ['log_scale', 'equal_date_size'], diff --git a/superset/assets/src/explore/controlPanels/Pie.js b/superset/assets/src/explore/controlPanels/Pie.js index ee9481b93..e3b120394 100644 --- a/superset/assets/src/explore/controlPanels/Pie.js +++ b/superset/assets/src/explore/controlPanels/Pie.js @@ -37,7 +37,7 @@ export default { ['pie_label_type', 'number_format'], ['donut', 'show_legend'], ['show_labels', 'labels_outside'], - ['color_scheme'], + ['color_scheme', 'label_colors'], ], }, ], diff --git a/superset/assets/src/explore/controlPanels/Rose.js b/superset/assets/src/explore/controlPanels/Rose.js index 5939d0cbe..135b0d5ad 100644 --- a/superset/assets/src/explore/controlPanels/Rose.js +++ b/superset/assets/src/explore/controlPanels/Rose.js @@ -27,7 +27,7 @@ export default { label: t('Chart Options'), expanded: true, controlSetRows: [ - ['color_scheme'], + ['color_scheme', 'label_colors'], ['number_format', 'date_time_format'], ['rich_tooltip', 'rose_area_proportion'], ], diff --git a/superset/assets/src/explore/controlPanels/Sankey.js b/superset/assets/src/explore/controlPanels/Sankey.js index dfb337ab7..c7c2f21b1 100644 --- a/superset/assets/src/explore/controlPanels/Sankey.js +++ b/superset/assets/src/explore/controlPanels/Sankey.js @@ -34,7 +34,7 @@ export default { label: t('Chart Options'), expanded: true, controlSetRows: [ - ['color_scheme'], + ['color_scheme', 'label_colors'], ], }, ], diff --git a/superset/assets/src/explore/controlPanels/Sunburst.js b/superset/assets/src/explore/controlPanels/Sunburst.js index 1272c83bc..6d84b2d47 100644 --- a/superset/assets/src/explore/controlPanels/Sunburst.js +++ b/superset/assets/src/explore/controlPanels/Sunburst.js @@ -35,7 +35,7 @@ export default { label: t('Chart Options'), expanded: true, controlSetRows: [ - ['color_scheme'], + ['color_scheme', 'label_colors'], ], }, ], diff --git a/superset/assets/src/explore/controlPanels/Treemap.js b/superset/assets/src/explore/controlPanels/Treemap.js index 9ed05d958..8342ee55b 100644 --- a/superset/assets/src/explore/controlPanels/Treemap.js +++ b/superset/assets/src/explore/controlPanels/Treemap.js @@ -34,7 +34,7 @@ export default { label: t('Chart Options'), expanded: true, controlSetRows: [ - ['color_scheme'], + ['color_scheme', 'label_colors'], ['treemap_ratio'], ['number_format'], ], diff --git a/superset/assets/src/explore/controlPanels/WordCloud.js b/superset/assets/src/explore/controlPanels/WordCloud.js index 7566a5a46..d7dd43de0 100644 --- a/superset/assets/src/explore/controlPanels/WordCloud.js +++ b/superset/assets/src/explore/controlPanels/WordCloud.js @@ -35,7 +35,7 @@ export default { controlSetRows: [ ['size_from', 'size_to'], ['rotation'], - ['color_scheme'], + ['color_scheme', 'label_colors'], ], }, ], diff --git a/superset/assets/src/explore/controlPanels/sections.jsx b/superset/assets/src/explore/controlPanels/sections.jsx index e3b32ca28..e4ad95c8f 100644 --- a/superset/assets/src/explore/controlPanels/sections.jsx +++ b/superset/assets/src/explore/controlPanels/sections.jsx @@ -42,7 +42,7 @@ export const datasourceAndVizType = { export const colorScheme = { label: t('Color Scheme'), controlSetRows: [ - ['color_scheme'], + ['color_scheme', 'label_colors'], ], }; diff --git a/superset/assets/src/explore/controls.jsx b/superset/assets/src/explore/controls.jsx index be1e56f04..1f63a23ca 100644 --- a/superset/assets/src/explore/controls.jsx +++ b/superset/assets/src/explore/controls.jsx @@ -2090,6 +2090,16 @@ export const controls = { schemes: () => categoricalSchemeRegistry.getMap(), }, + label_colors: { + type: 'ColorMapControl', + label: t('Color Map'), + default: {}, + mapStateToProps: state => ({ + colorNamespace: state.form_data.color_namespace, + colorScheme: state.form_data.color_scheme, + }), + }, + significance_level: { type: 'TextControl', label: t('Significance Level'), From 97718daea36765677ee3e79f66704a20e693e86c Mon Sep 17 00:00:00 2001 From: Maxime Beauchemin Date: Thu, 18 Apr 2019 11:36:38 -0700 Subject: [PATCH 38/38] Fix control validation handling (#7231) Fixes a series of unexpected things around control validation. * when a chart opens in a state where a control is invalid, it still runs the query, and sometimes gets stuck in what appears to be a 'running' state. After this change, no query is run, and a warning is displayed in the chart panel body, just like any other error would * validation used to be done in the component and alter the redux store as it went. Clearly this is not the right approach, now validation occurs on loading the initial redux state, as well as in the reducer when controls are changed * currently, when going from a invalid control state to a valid one (user addresses what is needed), it auto-triggers a query which can be unexpected. After this change, the error message disappears, and the "Run Query" overlay gets displayed * when changing viz type, it's common to get new validation errors, and currently when that occurs it will still go ahead and run a query with invalid inputs, which often results in errors that are not well handled, since much of the logic assumes control-validated input. * prettier control validation messages (cherry picked from commit a3212eba5df95bca834d8d6d98c11d522d9172f3) --- .gitignore | 1 + CONTRIBUTING.md | 6 +++ .../integration/explore/control.test.js | 18 ++++----- .../cypress/integration/explore/link.test.js | 20 +++++----- superset/assets/src/chart/Chart.jsx | 6 ++- superset/assets/src/chart/ChartRenderer.jsx | 3 +- .../assets/src/explore/components/Control.jsx | 35 +----------------- .../components/ExploreViewContainer.jsx | 10 ++++- .../src/explore/reducers/exploreReducer.js | 37 +++++++++++-------- .../src/explore/reducers/getInitialState.js | 4 +- superset/assets/src/explore/store.js | 20 +++++++++- 11 files changed, 84 insertions(+), 76 deletions(-) diff --git a/.gitignore b/.gitignore index 929abbd5e..2c553ad1d 100644 --- a/.gitignore +++ b/.gitignore @@ -40,6 +40,7 @@ dump.rdb env env_py3 envpy3 +env36 local_config.py superset_config.py superset.egg-info/ diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 5db5e98c8..36ed57337 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -520,6 +520,12 @@ Run Cypress tests: cd /superset/superset/assets npm run build npm run cypress run + +# run tests from a specific file +npm run cypress run -- --spec cypress/integration/explore/link.test.js + +# run specific file with video capture +npm run cypress run -- --spec cypress/integration/dashboard/index.test.js --config video=true ``` ## Translating diff --git a/superset/assets/cypress/integration/explore/control.test.js b/superset/assets/cypress/integration/explore/control.test.js index 711ab7834..d20cb46e2 100644 --- a/superset/assets/cypress/integration/explore/control.test.js +++ b/superset/assets/cypress/integration/explore/control.test.js @@ -29,7 +29,7 @@ describe('Groupby', () => { cy.route('GET', '/superset/explore_json/**').as('getJson'); cy.route('POST', '/superset/explore_json/**').as('postJson'); cy.visitChartByName('Num Births Trend'); - cy.verifySliceSuccess({ waitAlias: '@getJson' }); + cy.verifySliceSuccess({ waitAlias: '@postJson' }); cy.get('[data-test=groupby]').within(() => { cy.get('.Select-control').click(); @@ -53,7 +53,7 @@ describe('AdhocMetrics', () => { const metricName = 'Girl Births'; cy.visitChartByName('Num Births Trend'); - cy.verifySliceSuccess({ waitAlias: '@getJson' }); + cy.verifySliceSuccess({ waitAlias: '@postJson' }); cy.get('[data-test=metrics]').within(() => { cy.get('.select-clear').click(); @@ -86,7 +86,7 @@ describe('AdhocMetrics', () => { const metric = 'SUM(num)/COUNT(DISTINCT name)'; cy.visitChartByName('Num Births Trend'); - cy.verifySliceSuccess({ waitAlias: '@getJson' }); + cy.verifySliceSuccess({ waitAlias: '@postJson' }); cy.get('[data-test=metrics]').within(() => { cy.get('.select-clear').click(); @@ -115,7 +115,7 @@ describe('AdhocMetrics', () => { it('Switch between simple and custom sql tabs', () => { cy.visitChartByName('Num Births Trend'); - cy.verifySliceSuccess({ waitAlias: '@getJson' }); + cy.verifySliceSuccess({ waitAlias: '@postJson' }); cy.get('[data-test=metrics]').within(() => { cy.get('.select-clear').click(); @@ -155,7 +155,7 @@ describe('AdhocFilters', () => { it('Set simple adhoc filter', () => { cy.visitChartByName('Num Births Trend'); - cy.verifySliceSuccess({ waitAlias: '@getJson' }); + cy.verifySliceSuccess({ waitAlias: '@postJson' }); cy.get('[data-test=adhoc_filters]').within(() => { cy.get('.Select-control').click({ force: true }); @@ -187,7 +187,7 @@ describe('AdhocFilters', () => { it('Set custom adhoc filter', () => { cy.visitChartByName('Num Births Trend'); - cy.verifySliceSuccess({ waitAlias: '@getJson' }); + cy.verifySliceSuccess({ waitAlias: '@postJson' }); cy.get('[data-test=adhoc_filters]').within(() => { cy.get('.Select-control').click({ force: true }); @@ -226,7 +226,7 @@ describe('Advanced analytics', () => { it('Create custom time compare', () => { cy.visitChartByName('Num Births Trend'); - cy.verifySliceSuccess({ waitAlias: '@getJson' }); + cy.verifySliceSuccess({ waitAlias: '@postJson' }); cy.get('span') .contains('Advanced Analytics') @@ -247,7 +247,7 @@ describe('Advanced analytics', () => { cy.wait('@postJson'); cy.reload(); cy.verifySliceSuccess({ - waitAlias: '@getJson', + waitAlias: '@postJson', chartSelector: 'svg', }); @@ -267,7 +267,7 @@ describe('Annotations', () => { it('Create formula annotation y-axis goal line', () => { cy.visitChartByName('Num Births Trend'); - cy.verifySliceSuccess({ waitAlias: '@getJson' }); + cy.verifySliceSuccess({ waitAlias: '@postJson' }); cy.get('[data-test=annotation_layers]').within(() => { cy.get('button').click(); diff --git a/superset/assets/cypress/integration/explore/link.test.js b/superset/assets/cypress/integration/explore/link.test.js index 36b56ce5d..9f5f82d59 100644 --- a/superset/assets/cypress/integration/explore/link.test.js +++ b/superset/assets/cypress/integration/explore/link.test.js @@ -32,7 +32,7 @@ describe('Test explore links', () => { it('Open and close view query modal', () => { cy.visitChartByName('Growth Rate'); - cy.verifySliceSuccess({ waitAlias: '@getJson' }); + cy.verifySliceSuccess({ waitAlias: '@postJson' }); cy.get('button#query').click(); cy.get('span').contains('View query').parent().click(); @@ -48,7 +48,7 @@ describe('Test explore links', () => { cy.route('POST', 'r/shortner/').as('getShortUrl'); cy.visitChartByName('Growth Rate'); - cy.verifySliceSuccess({ waitAlias: '@getJson' }); + cy.verifySliceSuccess({ waitAlias: '@postJson' }); cy.get('[data-test=short-link-button]').click(); @@ -61,12 +61,12 @@ describe('Test explore links', () => { .then((text) => { cy.visit(text); }); - cy.verifySliceSuccess({ waitAlias: '@getJson' }); + cy.verifySliceSuccess({ waitAlias: '@postJson' }); }); it('Test iframe link', () => { cy.visitChartByName('Growth Rate'); - cy.verifySliceSuccess({ waitAlias: '@getJson' }); + cy.verifySliceSuccess({ waitAlias: '@postJson' }); cy.get('[data-test=embed-code-button]').click(); cy.get('#embed-code-popover').within(() => { @@ -94,14 +94,14 @@ describe('Test explore links', () => { cy.url().should('eq', url); cy.visitChartByName(newChartName); - cy.verifySliceSuccess({ waitAlias: '@getJson' }); + cy.verifySliceSuccess({ waitAlias: '@postJson' }); }); }); xit('Test chart save', () => { const chartName = 'Test chart'; cy.visitChartByName(chartName); - cy.verifySliceSuccess({ waitAlias: '@getJson' }); + cy.verifySliceSuccess({ waitAlias: '@postJson' }); cy.get('[data-test=groupby]').within(() => { cy.get('span.select-clear-zone').click(); @@ -118,7 +118,7 @@ describe('Test explore links', () => { it('Test chart save as and add to new dashboard', () => { cy.visitChartByName('Growth Rate'); - cy.verifySliceSuccess({ waitAlias: '@getJson' }); + cy.verifySliceSuccess({ waitAlias: '@postJson' }); const dashboardTitle = 'Test dashboard'; cy.get('button[data-target="#save_modal"]').click(); @@ -128,7 +128,7 @@ describe('Test explore links', () => { cy.get('input[placeholder="[dashboard name]"]').type(dashboardTitle); cy.get('button#btn_modal_save').click(); }); - cy.verifySliceSuccess({ waitAlias: '@getJson' }); + cy.verifySliceSuccess({ waitAlias: '@postJson' }); cy.request(`/dashboard/api/read?_flt_3_dashboard_title=${dashboardTitle}`).then((response) => { expect(response.body.pks[0]).not.equals(null); }); @@ -136,7 +136,7 @@ describe('Test explore links', () => { it('Test chart save as and add to existing dashboard', () => { cy.visitChartByName('Most Populated Countries'); - cy.verifySliceSuccess({ waitAlias: '@getJson' }); + cy.verifySliceSuccess({ waitAlias: '@postJson' }); const chartName = 'New Most Populated Countries'; const dashboardTitle = 'Test dashboard'; @@ -151,7 +151,7 @@ describe('Test explore links', () => { }); cy.get('button#btn_modal_save').click(); }); - cy.verifySliceSuccess({ waitAlias: '@getJson' }); + cy.verifySliceSuccess({ waitAlias: '@postJson' }); cy.request(`/chart/api/read?_flt_3_slice_name=${chartName}`).then((response) => { cy.request('DELETE', `/chart/api/delete/${response.body.pks[0]}`); }); diff --git a/superset/assets/src/chart/Chart.jsx b/superset/assets/src/chart/Chart.jsx index 8cb615361..c0d391673 100644 --- a/superset/assets/src/chart/Chart.jsx +++ b/superset/assets/src/chart/Chart.jsx @@ -18,6 +18,8 @@ */ import PropTypes from 'prop-types'; import React from 'react'; +import { Alert } from 'react-bootstrap'; + import { Logger, LOG_ACTIONS_RENDER_CHART_CONTAINER } from '../logger/LogUtils'; import Loading from '../components/Loading'; import RefreshChartOverlay from '../components/RefreshChartOverlay'; @@ -133,7 +135,9 @@ class Chart extends React.PureComponent { if (chartStatus === 'failed') { return this.renderStackTraceMessage(); } - + if (errorMessage) { + return {errorMessage}; + } return (
    {this.renderTooltip()} diff --git a/superset/assets/src/explore/components/Control.jsx b/superset/assets/src/explore/components/Control.jsx index 31942e9f2..b8babc709 100644 --- a/superset/assets/src/explore/components/Control.jsx +++ b/superset/assets/src/explore/components/Control.jsx @@ -37,7 +37,6 @@ const propTypes = { description: PropTypes.string, tooltipOnClick: PropTypes.func, places: PropTypes.number, - validators: PropTypes.array, validationErrors: PropTypes.array, renderTrigger: PropTypes.bool, rightNode: PropTypes.node, @@ -54,7 +53,6 @@ const propTypes = { const defaultProps = { renderTrigger: false, - validators: [], hidden: false, validationErrors: [], }; @@ -63,45 +61,14 @@ export default class Control extends React.PureComponent { constructor(props) { super(props); this.state = { hovered: false }; - this.validate = this.validate.bind(this); this.onChange = this.onChange.bind(this); } - componentDidMount() { - this.validateAndSetValue(this.props.value, []); - } onChange(value, errors) { - this.validateAndSetValue(value, errors); + this.props.actions.setControlValue(this.props.name, value, errors); } setHover(hovered) { this.setState({ hovered }); } - validateAndSetValue(value, errors) { - let validationErrors = this.props.validationErrors; - let currentErrors = this.validate(value); - if (errors && errors.length > 0) { - currentErrors = validationErrors.concat(errors); - } - if (validationErrors.length + currentErrors.length > 0) { - validationErrors = currentErrors; - } - - if (value !== this.props.value || validationErrors !== this.props.validationErrors) { - this.props.actions.setControlValue(this.props.name, value, validationErrors); - } - } - validate(value) { - const validators = this.props.validators; - const validationErrors = []; - if (validators && validators.length > 0) { - validators.forEach((f) => { - const v = f(value); - if (v) { - validationErrors.push(v); - } - }); - } - return validationErrors; - } render() { const ControlType = controlMap[this.props.type]; const divStyle = this.props.hidden ? { display: 'none' } : null; diff --git a/superset/assets/src/explore/components/ExploreViewContainer.jsx b/superset/assets/src/explore/components/ExploreViewContainer.jsx index 91777e45e..68d8fe1ad 100644 --- a/superset/assets/src/explore/components/ExploreViewContainer.jsx +++ b/superset/assets/src/explore/components/ExploreViewContainer.jsx @@ -21,6 +21,7 @@ import React from 'react'; import PropTypes from 'prop-types'; import { bindActionCreators } from 'redux'; import { connect } from 'react-redux'; +import { t } from '@superset-ui/translation'; import ExploreChartPanel from './ExploreChartPanel'; import ControlPanelsContainer from './ControlPanelsContainer'; @@ -100,6 +101,12 @@ class ExploreViewContainer extends React.Component { document.addEventListener('keydown', this.handleKeydown); this.addHistory({ isReplace: true }); this.props.actions.logEvent(LOG_ACTIONS_MOUNT_EXPLORER); + + // Trigger the chart if there are no errors + const { chart } = this.props; + if (!this.hasErrors()) { + this.props.actions.triggerQuery(true, this.props.chart.id); + } } componentWillReceiveProps(nextProps) { @@ -272,12 +279,13 @@ class ExploreViewContainer extends React.Component { renderErrorMessage() { // Returns an error message as a node if any errors are in the store const errors = []; + const ctrls = this.props.controls; for (const controlName in this.props.controls) { const control = this.props.controls[controlName]; if (control.validationErrors && control.validationErrors.length > 0) { errors.push(
    - {`[ ${control.label} ] `} + {t('Control labeled ')}{` "${control.label}" `} {control.validationErrors.join('. ')}
    , ); diff --git a/superset/assets/src/explore/reducers/exploreReducer.js b/superset/assets/src/explore/reducers/exploreReducer.js index 382388881..6f6a1a9e6 100644 --- a/superset/assets/src/explore/reducers/exploreReducer.js +++ b/superset/assets/src/explore/reducers/exploreReducer.js @@ -17,7 +17,8 @@ * under the License. */ /* eslint camelcase: 0 */ -import { getControlsState, getFormDataFromControls } from '../store'; +import { validateControl, getControlsState, getFormDataFromControls } from '../store'; +import controls from '../controls'; import * as actions from '../actions/exploreActions'; export default function exploreReducer(state = {}, action) { @@ -75,24 +76,28 @@ export default function exploreReducer(state = {}, action) { }; }, [actions.SET_FIELD_VALUE]() { - const controls = Object.assign({}, state.controls); - const control = Object.assign({}, controls[action.controlName]); - control.value = action.value; - control.validationErrors = action.validationErrors; - controls[action.controlName] = control; - const changes = { - controls, + // These errors are reported from the Control components + let errors = action.validationErrors || []; + let control = { + ...controls[action.controlName], + value: action.value, }; - if (control.renderTrigger) { - changes.triggerRender = true; - } else { - changes.triggerRender = false; - } - const newState = { + control = validateControl(control); + + // These errors are based on control config `validators` + errors = errors.concat(control.validationErrors || []); + const hasErrors = errors && errors.length > 0; + return { ...state, - ...changes, + triggerRender: control.renderTrigger && !hasErrors, + controls: { + ...state.controls, + [action.controlName]: { + ...control, + validationErrors: errors, + }, + }, }; - return newState; }, [actions.SET_EXPLORE_CONTROLS]() { return { diff --git a/superset/assets/src/explore/reducers/getInitialState.js b/superset/assets/src/explore/reducers/getInitialState.js index 48c85c757..98b979914 100644 --- a/superset/assets/src/explore/reducers/getInitialState.js +++ b/superset/assets/src/explore/reducers/getInitialState.js @@ -52,14 +52,14 @@ export default function getInitialState(bootstrapData) { [chartKey]: { id: chartKey, chartAlert: null, - chartStatus: 'loading', + chartStatus: null, chartUpdateEndTime: null, chartUpdateStartTime: 0, latestQueryFormData: getFormDataFromControls(controls), sliceFormData, queryController: null, queryResponse: null, - triggerQuery: true, + triggerQuery: false, lastRendered: 0, }, }, diff --git a/superset/assets/src/explore/store.js b/superset/assets/src/explore/store.js index be2be7b32..df456c29e 100644 --- a/superset/assets/src/explore/store.js +++ b/superset/assets/src/explore/store.js @@ -29,6 +29,24 @@ export function getFormDataFromControls(controlsState) { return formData; } +export function validateControl(control) { + const validators = control.validators; + const validationErrors = []; + if (validators && validators.length > 0) { + validators.forEach((f) => { + const v = f(control.value); + if (v) { + validationErrors.push(v); + } + }); + } + if (validationErrors.length > 0) { + return { ...control, validationErrors }; + } + return control; +} + + export function getControlNames(vizType, datasourceType) { const controlNames = []; sectionsToRender(vizType, datasourceType).forEach( @@ -109,7 +127,7 @@ export function getControlsState(state, form_data) { ) { control.value = formData[k]; } - controlsState[k] = control; + controlsState[k] = validateControl(control); }); if (viz.onInit) { return viz.onInit(controlsState);