mirror of
https://github.com/apache/superset.git
synced 2024-09-17 11:09:47 -04:00
Merge branch 'lyftga'
This commit is contained in:
commit
929fb6bbb8
@ -38,8 +38,8 @@ export default () => describe('top-level controls', () => {
|
|||||||
.forEach((slice) => {
|
.forEach((slice) => {
|
||||||
const sliceRequest = `getJson_${slice.slice_id}`;
|
const sliceRequest = `getJson_${slice.slice_id}`;
|
||||||
sliceRequests.push(`@${sliceRequest}`);
|
sliceRequests.push(`@${sliceRequest}`);
|
||||||
const formData = `{"slice_id":${slice.slice_id},"viz_type":"${slice.form_data.viz_type}"}`;
|
const formData = `{"slice_id":${slice.slice_id}}`;
|
||||||
cy.route('GET', `/superset/explore_json/?form_data=${formData}`).as(sliceRequest);
|
cy.route('POST', `/superset/explore_json/?form_data=${formData}`).as(sliceRequest);
|
||||||
|
|
||||||
const forceRefresh = `postJson_${slice.slice_id}_force`;
|
const forceRefresh = `postJson_${slice.slice_id}_force`;
|
||||||
forceRefreshRequests.push(`@${forceRefresh}`);
|
forceRefreshRequests.push(`@${forceRefresh}`);
|
||||||
|
@ -28,9 +28,9 @@ export default () => describe('edit mode', () => {
|
|||||||
const bootstrapData = JSON.parse(data[0].dataset.bootstrap);
|
const bootstrapData = JSON.parse(data[0].dataset.bootstrap);
|
||||||
const dashboard = bootstrapData.dashboard_data;
|
const dashboard = bootstrapData.dashboard_data;
|
||||||
const boxplotChartId = dashboard.slices.find(slice => (slice.form_data.viz_type === 'box_plot')).slice_id;
|
const boxplotChartId = dashboard.slices.find(slice => (slice.form_data.viz_type === 'box_plot')).slice_id;
|
||||||
const formData = `{"slice_id":${boxplotChartId},"viz_type":"box_plot"}`;
|
const formData = `{"slice_id":${boxplotChartId}}`;
|
||||||
const boxplotRequest = `/superset/explore_json/?form_data=${formData}`;
|
const boxplotRequest = `/superset/explore_json/?form_data=${formData}`;
|
||||||
cy.route('GET', boxplotRequest).as('boxplotRequest');
|
cy.route('POST', boxplotRequest).as('boxplotRequest');
|
||||||
});
|
});
|
||||||
|
|
||||||
cy.get('.dashboard-header').contains('Edit dashboard').click();
|
cy.get('.dashboard-header').contains('Edit dashboard').click();
|
||||||
|
@ -39,9 +39,9 @@ export default () => describe('dashboard filter', () => {
|
|||||||
it('should apply filter', () => {
|
it('should apply filter', () => {
|
||||||
const aliases = [];
|
const aliases = [];
|
||||||
|
|
||||||
const formData = `{"slice_id":${filterId},"viz_type":"filter_box"}`;
|
const formData = `{"slice_id":${filterId}}`;
|
||||||
const filterRoute = `/superset/explore_json/?form_data=${formData}`;
|
const filterRoute = `/superset/explore_json/?form_data=${formData}`;
|
||||||
cy.route('GET', filterRoute).as('fetchFilter');
|
cy.route('POST', filterRoute).as('fetchFilter');
|
||||||
cy.wait('@fetchFilter');
|
cy.wait('@fetchFilter');
|
||||||
sliceIds
|
sliceIds
|
||||||
.filter(id => (parseInt(id, 10) !== filterId))
|
.filter(id => (parseInt(id, 10) !== filterId))
|
||||||
|
@ -34,8 +34,8 @@ export default () => describe('load', () => {
|
|||||||
// then define routes and create alias for each requests
|
// then define routes and create alias for each requests
|
||||||
slices.forEach((slice) => {
|
slices.forEach((slice) => {
|
||||||
const alias = `getJson_${slice.slice_id}`;
|
const alias = `getJson_${slice.slice_id}`;
|
||||||
const formData = `{"slice_id":${slice.slice_id},"viz_type":"${slice.form_data.viz_type}"}`;
|
const formData = `{"slice_id":${slice.slice_id}}`;
|
||||||
cy.route('GET', `/superset/explore_json/?form_data=${formData}`).as(alias);
|
cy.route('POST', `/superset/explore_json/?form_data=${formData}`).as(alias);
|
||||||
aliases.push(`@${alias}`);
|
aliases.push(`@${alias}`);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -56,9 +56,9 @@ export default () => describe('save', () => {
|
|||||||
cy.wait('@copyRequest');
|
cy.wait('@copyRequest');
|
||||||
|
|
||||||
// should have box_plot chart
|
// should have box_plot chart
|
||||||
const formData = `{"slice_id":${boxplotChartId},"viz_type":"box_plot"}`;
|
const formData = `{"slice_id":${boxplotChartId}}`;
|
||||||
const boxplotRequest = `/superset/explore_json/?form_data=${formData}`;
|
const boxplotRequest = `/superset/explore_json/?form_data=${formData}`;
|
||||||
cy.route('GET', boxplotRequest).as('boxplotRequest');
|
cy.route('POST', boxplotRequest).as('boxplotRequest');
|
||||||
cy.wait('@boxplotRequest');
|
cy.wait('@boxplotRequest');
|
||||||
cy.get('.grid-container .box_plot').should('be.exist');
|
cy.get('.grid-container .box_plot').should('be.exist');
|
||||||
|
|
||||||
|
@ -20,6 +20,7 @@ import PropTypes from 'prop-types';
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { Alert } from 'react-bootstrap';
|
import { Alert } from 'react-bootstrap';
|
||||||
|
|
||||||
|
import { isFeatureEnabled, FeatureFlag } from 'src/featureFlags';
|
||||||
import { Logger, LOG_ACTIONS_RENDER_CHART_CONTAINER } from '../logger/LogUtils';
|
import { Logger, LOG_ACTIONS_RENDER_CHART_CONTAINER } from '../logger/LogUtils';
|
||||||
import Loading from '../components/Loading';
|
import Loading from '../components/Loading';
|
||||||
import RefreshChartOverlay from '../components/RefreshChartOverlay';
|
import RefreshChartOverlay from '../components/RefreshChartOverlay';
|
||||||
@ -70,7 +71,7 @@ class Chart extends React.PureComponent {
|
|||||||
}
|
}
|
||||||
componentDidMount() {
|
componentDidMount() {
|
||||||
if (this.props.triggerQuery) {
|
if (this.props.triggerQuery) {
|
||||||
if (this.props.chartId > 0) {
|
if (this.props.chartId > 0 && isFeatureEnabled(FeatureFlag.CLIENT_CACHE)) {
|
||||||
// Load saved chart with a GET request
|
// Load saved chart with a GET request
|
||||||
this.props.actions.getSavedChart(
|
this.props.actions.getSavedChart(
|
||||||
this.props.formData,
|
this.props.formData,
|
||||||
|
@ -21,6 +21,7 @@
|
|||||||
/* eslint no-param-reassign: ["error", { "props": false }] */
|
/* eslint no-param-reassign: ["error", { "props": false }] */
|
||||||
import { t } from '@superset-ui/translation';
|
import { t } from '@superset-ui/translation';
|
||||||
import { SupersetClient } from '@superset-ui/connection';
|
import { SupersetClient } from '@superset-ui/connection';
|
||||||
|
import { isFeatureEnabled, FeatureFlag } from 'src/featureFlags';
|
||||||
import { getExploreUrlAndPayload, getAnnotationJsonUrl } from '../explore/exploreUtils';
|
import { getExploreUrlAndPayload, getAnnotationJsonUrl } from '../explore/exploreUtils';
|
||||||
import { requiresQuery, ANNOTATION_SOURCE_TYPES } from '../modules/AnnotationTypes';
|
import { requiresQuery, ANNOTATION_SOURCE_TYPES } from '../modules/AnnotationTypes';
|
||||||
import { addDangerToast } from '../messageToasts/actions';
|
import { addDangerToast } from '../messageToasts/actions';
|
||||||
@ -194,7 +195,9 @@ export function exploreJSON(formData, force = false, timeout = 60, key, method)
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
const clientMethod = method === 'GET' ? SupersetClient.get : SupersetClient.post;
|
const clientMethod = method === 'GET' && isFeatureEnabled(FeatureFlag.CLIENT_CACHE)
|
||||||
|
? SupersetClient.get
|
||||||
|
: SupersetClient.post;
|
||||||
const queryPromise = clientMethod(querySettings)
|
const queryPromise = clientMethod(querySettings)
|
||||||
.then(({ json }) => {
|
.then(({ json }) => {
|
||||||
dispatch(logEvent(LOG_ACTIONS_LOAD_CHART, {
|
dispatch(logEvent(LOG_ACTIONS_LOAD_CHART, {
|
||||||
|
@ -222,7 +222,7 @@ class Header extends React.PureComponent {
|
|||||||
colorScheme,
|
colorScheme,
|
||||||
colorNamespace,
|
colorNamespace,
|
||||||
);
|
);
|
||||||
const labelColors = scale.getColorMap();
|
const labelColors = colorScheme ? scale.getColorMap() : {};
|
||||||
const data = {
|
const data = {
|
||||||
positions,
|
positions,
|
||||||
expanded_slices: expandedSlices,
|
expanded_slices: expandedSlices,
|
||||||
|
@ -110,7 +110,7 @@ class SaveModal extends React.PureComponent {
|
|||||||
colorScheme,
|
colorScheme,
|
||||||
colorNamespace,
|
colorNamespace,
|
||||||
);
|
);
|
||||||
const labelColors = scale.getColorMap();
|
const labelColors = colorScheme ? scale.getColorMap() : {};
|
||||||
const data = {
|
const data = {
|
||||||
positions,
|
positions,
|
||||||
css,
|
css,
|
||||||
|
@ -21,6 +21,7 @@
|
|||||||
export enum FeatureFlag {
|
export enum FeatureFlag {
|
||||||
SCOPED_FILTER = 'SCOPED_FILTER',
|
SCOPED_FILTER = 'SCOPED_FILTER',
|
||||||
OMNIBAR = 'OMNIBAR',
|
OMNIBAR = 'OMNIBAR',
|
||||||
|
CLIENT_CACHE = 'CLIENT_CACHE',
|
||||||
}
|
}
|
||||||
|
|
||||||
export type FeatureFlagMap = {
|
export type FeatureFlagMap = {
|
||||||
|
@ -200,7 +200,10 @@ LANGUAGES = {
|
|||||||
# For example, DEFAULT_FEATURE_FLAGS = { 'FOO': True, 'BAR': False } here
|
# For example, DEFAULT_FEATURE_FLAGS = { 'FOO': True, 'BAR': False } here
|
||||||
# and FEATURE_FLAGS = { 'BAR': True, 'BAZ': True } in superset_config.py
|
# and FEATURE_FLAGS = { 'BAR': True, 'BAZ': True } in superset_config.py
|
||||||
# will result in combined feature flags of { 'FOO': True, 'BAR': True, 'BAZ': True }
|
# will result in combined feature flags of { 'FOO': True, 'BAR': True, 'BAZ': True }
|
||||||
DEFAULT_FEATURE_FLAGS = {}
|
DEFAULT_FEATURE_FLAGS = {
|
||||||
|
# Experimental feature introducing a client (browser) cache
|
||||||
|
'CLIENT_CACHE': False,
|
||||||
|
}
|
||||||
|
|
||||||
# A function that receives a dict of all feature flags
|
# A function that receives a dict of all feature flags
|
||||||
# (DEFAULT_FEATURE_FLAGS merged with FEATURE_FLAGS)
|
# (DEFAULT_FEATURE_FLAGS merged with FEATURE_FLAGS)
|
||||||
|
@ -749,6 +749,10 @@ class Database(Model, AuditMixinNullable, ImportMixin):
|
|||||||
def table_cache_timeout(self):
|
def table_cache_timeout(self):
|
||||||
return self.metadata_cache_timeout.get('table_cache_timeout')
|
return self.metadata_cache_timeout.get('table_cache_timeout')
|
||||||
|
|
||||||
|
@property
|
||||||
|
def default_schemas(self):
|
||||||
|
return self.get_extra().get('default_schemas', [])
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def get_password_masked_url_from_uri(cls, uri):
|
def get_password_masked_url_from_uri(cls, uri):
|
||||||
url = make_url(uri)
|
url = make_url(uri)
|
||||||
|
@ -1590,6 +1590,16 @@ class Superset(BaseSupersetView):
|
|||||||
table_names = [tn for tn in table_names if substr in tn]
|
table_names = [tn for tn in table_names if substr in tn]
|
||||||
view_names = [vn for vn in view_names if substr in vn]
|
view_names = [vn for vn in view_names if substr in vn]
|
||||||
|
|
||||||
|
if not schema and database.default_schemas:
|
||||||
|
def get_schema(tbl_or_view_name):
|
||||||
|
return tbl_or_view_name.split('.')[0] if '.' in tbl_or_view_name else None
|
||||||
|
|
||||||
|
user_schema = g.user.email.split('@')[0]
|
||||||
|
valid_schemas = set(database.default_schemas + [user_schema])
|
||||||
|
|
||||||
|
table_names = [tn for tn in table_names if get_schema(tn) in valid_schemas]
|
||||||
|
view_names = [vn for vn in view_names if get_schema(vn) in valid_schemas]
|
||||||
|
|
||||||
max_items = config.get('MAX_TABLE_NAMES') or len(table_names)
|
max_items = config.get('MAX_TABLE_NAMES') or len(table_names)
|
||||||
total_items = len(table_names) + len(view_names)
|
total_items = len(table_names) + len(view_names)
|
||||||
max_tables = len(table_names)
|
max_tables = len(table_names)
|
||||||
|
@ -1112,16 +1112,22 @@ class NVD3TimeSeriesViz(NVD3Viz):
|
|||||||
series_title = series_title + (title_suffix,)
|
series_title = series_title + (title_suffix,)
|
||||||
|
|
||||||
values = []
|
values = []
|
||||||
|
non_nan_cnt = 0
|
||||||
for ds in df.index:
|
for ds in df.index:
|
||||||
if ds in ys:
|
if ds in ys:
|
||||||
d = {
|
d = {
|
||||||
'x': ds,
|
'x': ds,
|
||||||
'y': ys[ds],
|
'y': ys[ds],
|
||||||
}
|
}
|
||||||
|
if not np.isnan(ys[ds]):
|
||||||
|
non_nan_cnt += 1
|
||||||
else:
|
else:
|
||||||
d = {}
|
d = {}
|
||||||
values.append(d)
|
values.append(d)
|
||||||
|
|
||||||
|
if non_nan_cnt == 0:
|
||||||
|
continue
|
||||||
|
|
||||||
d = {
|
d = {
|
||||||
'key': series_title,
|
'key': series_title,
|
||||||
'values': values,
|
'values': values,
|
||||||
@ -1224,7 +1230,9 @@ class NVD3TimeSeriesViz(NVD3Viz):
|
|||||||
comparison_type = fd.get('comparison_type') or 'values'
|
comparison_type = fd.get('comparison_type') or 'values'
|
||||||
df = self.process_data(df)
|
df = self.process_data(df)
|
||||||
if comparison_type == 'values':
|
if comparison_type == 'values':
|
||||||
chart_data = self.to_series(df)
|
# Filter out series with all NaN
|
||||||
|
chart_data = self.to_series(df.dropna(axis=1, how='all'))
|
||||||
|
|
||||||
for i, (label, df2) in enumerate(self._extra_chart_data):
|
for i, (label, df2) in enumerate(self._extra_chart_data):
|
||||||
chart_data.extend(
|
chart_data.extend(
|
||||||
self.to_series(
|
self.to_series(
|
||||||
|
Loading…
Reference in New Issue
Block a user