refactor: migrate table chart to new API (#10270)

* refactor: migrate table chart to new API

* chore: bump superset-ui to 0.17.0

* Fix Cypress tests

* Apply soft-conversion to numeric metrics

Fix time column formatting test

* Add translation to chart does not exist error

* Bump to 0.17.1
This commit is contained in:
Jesse Yang 2021-01-29 03:12:09 -08:00 committed by GitHub
parent bab86abd92
commit e3db935c62
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
24 changed files with 680 additions and 390 deletions

View File

@ -362,7 +362,7 @@ ignored-argument-names=_.*
max-locals=15 max-locals=15
# Maximum number of return / yield for function / method body # Maximum number of return / yield for function / method body
max-returns=6 max-returns=10
# Maximum number of branch for function / method body # Maximum number of branch for function / method body
max-branches=12 max-branches=12

View File

@ -20,19 +20,20 @@ import {
getChartAliases, getChartAliases,
isLegacyResponse, isLegacyResponse,
getSliceIdFromRequestUrl, getSliceIdFromRequestUrl,
JsonObject,
} from '../../utils/vizPlugins'; } from '../../utils/vizPlugins';
import { WORLD_HEALTH_DASHBOARD } from './dashboard.helper'; import { WORLD_HEALTH_DASHBOARD } from './dashboard.helper';
describe('Dashboard load', () => { describe('Dashboard load', () => {
let dashboard; let dashboard;
let aliases; let aliases: string[];
beforeEach(() => { beforeEach(() => {
cy.login(); cy.login();
cy.visit(WORLD_HEALTH_DASHBOARD); cy.visit(WORLD_HEALTH_DASHBOARD);
cy.get('#app').then(data => { cy.get('#app').then(nodes => {
const bootstrapData = JSON.parse(data[0].dataset.bootstrap); const bootstrapData = JSON.parse(nodes[0].dataset.bootstrap || '');
dashboard = bootstrapData.dashboard_data; dashboard = bootstrapData.dashboard_data;
const { slices } = dashboard; const { slices } = dashboard;
// then define routes and create alias for each requests // then define routes and create alias for each requests
@ -53,7 +54,7 @@ describe('Dashboard load', () => {
sliceId = responseBody.form_data.slice_id; sliceId = responseBody.form_data.slice_id;
} else { } else {
sliceId = getSliceIdFromRequestUrl(request.url); sliceId = getSliceIdFromRequestUrl(request.url);
responseBody.result.forEach(element => { responseBody.result.forEach((element: JsonObject) => {
expect(element).to.have.property('error', null); expect(element).to.have.property('error', null);
expect(element).to.have.property('status', 'success'); expect(element).to.have.property('status', 'success');
}); });

View File

@ -16,7 +16,7 @@
* specific language governing permissions and limitations * specific language governing permissions and limitations
* under the License. * under the License.
*/ */
import { interceptChart, parsePostForm } from 'cypress/utils'; import { interceptChart, parsePostForm, Slice } from 'cypress/utils';
import { TABBED_DASHBOARD } from './dashboard.helper'; import { TABBED_DASHBOARD } from './dashboard.helper';
describe('Dashboard tabs', () => { describe('Dashboard tabs', () => {
@ -40,24 +40,28 @@ describe('Dashboard tabs', () => {
cy.visit(TABBED_DASHBOARD); cy.visit(TABBED_DASHBOARD);
cy.get('#app').then(data => { cy.get('#app').then(data => {
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 as { slices: Slice[] };
filterId = dashboard.slices.find( filterId = dashboard.slices.find(
slice => slice.form_data.viz_type === 'filter_box', slice => slice.form_data.viz_type === 'filter_box',
).slice_id; )?.slice_id;
boxplotId = dashboard.slices.find( boxplotId = dashboard.slices.find(
slice => slice.form_data.viz_type === 'box_plot', slice => slice.form_data.viz_type === 'box_plot',
).slice_id; )?.slice_id;
treemapId = dashboard.slices.find( treemapId = dashboard.slices.find(
slice => slice.form_data.viz_type === 'treemap', slice => slice.form_data.viz_type === 'treemap',
).slice_id; )?.slice_id;
linechartId = dashboard.slices.find( linechartId = dashboard.slices.find(
slice => slice.form_data.viz_type === 'line', slice => slice.form_data.viz_type === 'line',
).slice_id; )?.slice_id;
interceptChart(filterId).as('filterRequest'); interceptChart({ sliceId: filterId, legacy: true }).as('filterRequest');
interceptChart(treemapId).as('treemapRequest'); interceptChart({ sliceId: treemapId, legacy: true }).as('treemapRequest');
interceptChart(linechartId).as('linechartRequest'); interceptChart({ sliceId: linechartId, legacy: true }).as(
interceptChart(boxplotId, false).as('boxplotRequest'); 'linechartRequest',
);
interceptChart({ sliceId: boxplotId, legacy: false }).as(
'boxplotRequest',
);
}); });
}); });
@ -140,7 +144,7 @@ describe('Dashboard tabs', () => {
// send new query from same tab // send new query from same tab
cy.wait('@treemapRequest').then(({ request }) => { cy.wait('@treemapRequest').then(({ request }) => {
const requestBody = parsePostForm(request.body); const requestBody = parsePostForm(request.body);
const requestParams = JSON.parse(requestBody.form_data); const requestParams = JSON.parse(requestBody.form_data as string);
expect(requestParams.extra_filters[0]).deep.eq({ expect(requestParams.extra_filters[0]).deep.eq({
col: 'region', col: 'region',
op: '==', op: '==',
@ -153,7 +157,7 @@ describe('Dashboard tabs', () => {
cy.wait('@linechartRequest').then(({ request }) => { cy.wait('@linechartRequest').then(({ request }) => {
const requestBody = parsePostForm(request.body); const requestBody = parsePostForm(request.body);
const requestParams = JSON.parse(requestBody.form_data); const requestParams = JSON.parse(requestBody.form_data as string);
expect(requestParams.extra_filters[0]).deep.eq({ expect(requestParams.extra_filters[0]).deep.eq({
col: 'region', col: 'region',
op: '==', op: '==',

View File

@ -20,12 +20,14 @@ import {
isLegacyResponse, isLegacyResponse,
getChartAliases, getChartAliases,
parsePostForm, parsePostForm,
Dashboard,
JsonObject,
} from 'cypress/utils'; } from 'cypress/utils';
import { WORLD_HEALTH_DASHBOARD } from './dashboard.helper'; import { WORLD_HEALTH_DASHBOARD } from './dashboard.helper';
describe('Dashboard form data', () => { describe('Dashboard form data', () => {
const urlParams = { param1: '123', param2: 'abc' }; const urlParams = { param1: '123', param2: 'abc' };
let dashboard; let dashboard: Dashboard;
beforeEach(() => { beforeEach(() => {
cy.login(); cy.login();
@ -33,7 +35,7 @@ describe('Dashboard form data', () => {
cy.visit(WORLD_HEALTH_DASHBOARD, { qs: urlParams }); cy.visit(WORLD_HEALTH_DASHBOARD, { qs: urlParams });
cy.get('#app').then(data => { cy.get('#app').then(data => {
const bootstrapData = JSON.parse(data[0].dataset.bootstrap); const bootstrapData = JSON.parse(data[0].dataset.bootstrap || '');
dashboard = bootstrapData.dashboard_data; dashboard = bootstrapData.dashboard_data;
}); });
}); });
@ -47,13 +49,16 @@ describe('Dashboard form data', () => {
const responseBody = response?.body; const responseBody = response?.body;
if (isLegacyResponse(responseBody)) { if (isLegacyResponse(responseBody)) {
const requestParams = JSON.parse( const requestParams = JSON.parse(
parsePostForm(request.body).form_data, parsePostForm(request.body).form_data as string,
); );
expect(requestParams.url_params).deep.eq(urlParams); expect(requestParams.url_params).deep.eq(urlParams);
} else { } else {
request.body.queries.forEach(query => { // TODO: export url params to chart data API
request.body.queries.forEach(
(query: { url_params: JsonObject }) => {
expect(query.url_params).deep.eq(urlParams); expect(query.url_params).deep.eq(urlParams);
}); },
);
} }
}), }),
), ),

View File

@ -19,6 +19,7 @@
// *********************************************** // ***********************************************
// Tests for setting controls in the UI // Tests for setting controls in the UI
// *********************************************** // ***********************************************
import { interceptChart } from 'cypress/utils';
import { FORM_DATA_DEFAULTS, NUM_METRIC } from './visualizations/shared.helper'; import { FORM_DATA_DEFAULTS, NUM_METRIC } from './visualizations/shared.helper';
describe('Datasource control', () => { describe('Datasource control', () => {
@ -29,10 +30,10 @@ describe('Datasource control', () => {
let numScripts = 0; let numScripts = 0;
cy.login(); cy.login();
cy.intercept('GET', '/superset/explore_json/**').as('getJson'); interceptChart({ legacy: false }).as('chartData');
cy.intercept('POST', '/superset/explore_json/**').as('postJson');
cy.visitChartByName('Num Births Trend'); cy.visitChartByName('Num Births Trend');
cy.verifySliceSuccess({ waitAlias: '@postJson' }); cy.verifySliceSuccess({ waitAlias: '@chartData' });
cy.get('[data-test="open-datasource-tab').click({ force: true }); cy.get('[data-test="open-datasource-tab').click({ force: true });
cy.get('[data-test="datasource-menu-trigger"]').click(); cy.get('[data-test="datasource-menu-trigger"]').click();
@ -90,13 +91,13 @@ describe('Datasource control', () => {
describe('VizType control', () => { describe('VizType control', () => {
beforeEach(() => { beforeEach(() => {
cy.login(); cy.login();
cy.intercept('GET', '/superset/explore_json/**').as('getJson'); interceptChart({ legacy: false }).as('tableChartData');
cy.intercept('POST', '/superset/explore_json/**').as('postJson'); interceptChart({ legacy: true }).as('lineChartData');
}); });
it('Can change vizType', () => { it('Can change vizType', () => {
cy.visitChartByName('Daily Totals'); cy.visitChartByName('Daily Totals');
cy.verifySliceSuccess({ waitAlias: '@postJson' }); cy.verifySliceSuccess({ waitAlias: '@tableChartData' });
let numScripts = 0; let numScripts = 0;
cy.get('script').then(nodes => { cy.get('script').then(nodes => {
@ -114,27 +115,29 @@ describe('VizType control', () => {
}); });
cy.get('button[data-test="run-query-button"]').click(); cy.get('button[data-test="run-query-button"]').click();
cy.verifySliceSuccess({ waitAlias: '@postJson', chartSelector: 'svg' }); cy.verifySliceSuccess({
waitAlias: '@lineChartData',
chartSelector: 'svg',
});
}); });
}); });
describe('Time range filter', () => { describe('Time range filter', () => {
beforeEach(() => { beforeEach(() => {
cy.login(); cy.login();
cy.intercept('GET', '/superset/explore_json/**').as('getJson'); interceptChart({ legacy: true }).as('chartData');
cy.intercept('POST', '/superset/explore_json/**').as('postJson');
}); });
it('Advanced time_range params', () => { it('Advanced time_range params', () => {
const formData = { const formData = {
...FORM_DATA_DEFAULTS, ...FORM_DATA_DEFAULTS,
metrics: [NUM_METRIC],
viz_type: 'line', viz_type: 'line',
time_range: '100 years ago : now', time_range: '100 years ago : now',
metrics: [NUM_METRIC],
}; };
cy.visitChartByParams(JSON.stringify(formData)); cy.visitChartByParams(JSON.stringify(formData));
cy.verifySliceSuccess({ waitAlias: '@postJson' }); cy.verifySliceSuccess({ waitAlias: '@chartData' });
cy.get('[data-test=time-range-trigger]') cy.get('[data-test=time-range-trigger]')
.click() .click()
@ -152,13 +155,13 @@ describe('Time range filter', () => {
it('Common time_range params', () => { it('Common time_range params', () => {
const formData = { const formData = {
...FORM_DATA_DEFAULTS, ...FORM_DATA_DEFAULTS,
metrics: [NUM_METRIC],
viz_type: 'line', viz_type: 'line',
metrics: [NUM_METRIC],
time_range: 'Last year', time_range: 'Last year',
}; };
cy.visitChartByParams(JSON.stringify(formData)); cy.visitChartByParams(JSON.stringify(formData));
cy.verifySliceSuccess({ waitAlias: '@postJson' }); cy.verifySliceSuccess({ waitAlias: '@chartData' });
cy.get('[data-test=time-range-trigger]') cy.get('[data-test=time-range-trigger]')
.click() .click()
@ -172,13 +175,13 @@ describe('Time range filter', () => {
it('Previous time_range params', () => { it('Previous time_range params', () => {
const formData = { const formData = {
...FORM_DATA_DEFAULTS, ...FORM_DATA_DEFAULTS,
metrics: [NUM_METRIC],
viz_type: 'line', viz_type: 'line',
metrics: [NUM_METRIC],
time_range: 'previous calendar month', time_range: 'previous calendar month',
}; };
cy.visitChartByParams(JSON.stringify(formData)); cy.visitChartByParams(JSON.stringify(formData));
cy.verifySliceSuccess({ waitAlias: '@postJson' }); cy.verifySliceSuccess({ waitAlias: '@chartData' });
cy.get('[data-test=time-range-trigger]') cy.get('[data-test=time-range-trigger]')
.click() .click()
@ -192,13 +195,13 @@ describe('Time range filter', () => {
it('Custom time_range params', () => { it('Custom time_range params', () => {
const formData = { const formData = {
...FORM_DATA_DEFAULTS, ...FORM_DATA_DEFAULTS,
metrics: [NUM_METRIC],
viz_type: 'line', viz_type: 'line',
metrics: [NUM_METRIC],
time_range: 'DATEADD(DATETIME("today"), -7, day) : today', time_range: 'DATEADD(DATETIME("today"), -7, day) : today',
}; };
cy.visitChartByParams(JSON.stringify(formData)); cy.visitChartByParams(JSON.stringify(formData));
cy.verifySliceSuccess({ waitAlias: '@postJson' }); cy.verifySliceSuccess({ waitAlias: '@chartData' });
cy.get('[data-test=time-range-trigger]') cy.get('[data-test=time-range-trigger]')
.click() .click()
@ -215,13 +218,13 @@ describe('Time range filter', () => {
it('No filter time_range params', () => { it('No filter time_range params', () => {
const formData = { const formData = {
...FORM_DATA_DEFAULTS, ...FORM_DATA_DEFAULTS,
metrics: [NUM_METRIC],
viz_type: 'line', viz_type: 'line',
metrics: [NUM_METRIC],
time_range: 'No filter', time_range: 'No filter',
}; };
cy.visitChartByParams(JSON.stringify(formData)); cy.visitChartByParams(JSON.stringify(formData));
cy.verifySliceSuccess({ waitAlias: '@postJson' }); cy.verifySliceSuccess({ waitAlias: '@chartData' });
cy.get('[data-test=time-range-trigger]') cy.get('[data-test=time-range-trigger]')
.click() .click()
@ -235,16 +238,16 @@ describe('Time range filter', () => {
describe('Groupby control', () => { describe('Groupby control', () => {
it('Set groupby', () => { it('Set groupby', () => {
cy.login(); cy.login();
cy.intercept('GET', '/superset/explore_json/**').as('getJson'); interceptChart({ legacy: true }).as('chartData');
cy.intercept('POST', '/superset/explore_json/**').as('postJson');
cy.visitChartByName('Num Births Trend'); cy.visitChartByName('Num Births Trend');
cy.verifySliceSuccess({ waitAlias: '@postJson' }); cy.verifySliceSuccess({ waitAlias: '@chartData' });
cy.get('[data-test=groupby]').within(() => { cy.get('[data-test=groupby]').within(() => {
cy.get('.Select__control').click(); cy.get('.Select__control').click();
cy.get('input[type=text]').type('state{enter}'); cy.get('input[type=text]').type('state{enter}');
}); });
cy.get('button[data-test="run-query-button"]').click(); cy.get('button[data-test="run-query-button"]').click();
cy.verifySliceSuccess({ waitAlias: '@postJson', chartSelector: 'svg' }); cy.verifySliceSuccess({ waitAlias: '@chartData', chartSelector: 'svg' });
}); });
}); });

View File

@ -22,25 +22,25 @@
import rison from 'rison'; import rison from 'rison';
import shortid from 'shortid'; import shortid from 'shortid';
import { interceptChart } from 'cypress/utils';
import { HEALTH_POP_FORM_DATA_DEFAULTS } from './visualizations/shared.helper'; import { HEALTH_POP_FORM_DATA_DEFAULTS } from './visualizations/shared.helper';
const apiURL = (endpoint, queryObject) => const apiURL = (endpoint: string, queryObject: Record<string, unknown>) =>
`${endpoint}?q=${rison.encode(queryObject)}`; `${endpoint}?q=${rison.encode(queryObject)}`;
describe('Test explore links', () => { describe('Test explore links', () => {
beforeEach(() => { beforeEach(() => {
cy.login(); cy.login();
cy.intercept('GET', '/superset/explore_json/**').as('getJson'); interceptChart({ legacy: true }).as('chartData');
cy.intercept('POST', '/superset/explore_json/**').as('postJson');
}); });
it('Open and close view query modal', () => { it('Open and close view query modal', () => {
cy.visitChartByName('Growth Rate'); cy.visitChartByName('Growth Rate');
cy.verifySliceSuccess({ waitAlias: '@postJson' }); cy.verifySliceSuccess({ waitAlias: '@chartData' });
cy.get('button#query').click(); cy.get('button#query').click();
cy.get('span').contains('View query').parent().click(); cy.get('span').contains('View query').parent().click();
cy.wait('@postJson').then(() => { cy.wait('@chartData').then(() => {
cy.get('code'); cy.get('code');
}); });
cy.get('.ant-modal-content').within(() => { cy.get('.ant-modal-content').within(() => {
@ -52,7 +52,7 @@ describe('Test explore links', () => {
cy.intercept('POST', 'r/shortner/').as('getShortUrl'); cy.intercept('POST', 'r/shortner/').as('getShortUrl');
cy.visitChartByName('Growth Rate'); cy.visitChartByName('Growth Rate');
cy.verifySliceSuccess({ waitAlias: '@postJson' }); cy.verifySliceSuccess({ waitAlias: '@chartData' });
cy.get('[data-test=short-link-button]').click(); cy.get('[data-test=short-link-button]').click();
@ -64,12 +64,12 @@ describe('Test explore links', () => {
.then(text => { .then(text => {
cy.visit(text); cy.visit(text);
}); });
cy.verifySliceSuccess({ waitAlias: '@postJson' }); cy.verifySliceSuccess({ waitAlias: '@chartData' });
}); });
it('Test iframe link', () => { it('Test iframe link', () => {
cy.visitChartByName('Growth Rate'); cy.visitChartByName('Growth Rate');
cy.verifySliceSuccess({ waitAlias: '@postJson' }); cy.verifySliceSuccess({ waitAlias: '@chartData' });
cy.get('[data-test=embed-code-button]').click(); cy.get('[data-test=embed-code-button]').click();
cy.get('#embed-code-popover').within(() => { cy.get('#embed-code-popover').within(() => {
@ -78,6 +78,8 @@ describe('Test explore links', () => {
}); });
it('Test chart save as AND overwrite', () => { it('Test chart save as AND overwrite', () => {
interceptChart({ legacy: false }).as('tableChartData');
const formData = { const formData = {
...HEALTH_POP_FORM_DATA_DEFAULTS, ...HEALTH_POP_FORM_DATA_DEFAULTS,
viz_type: 'table', viz_type: 'table',
@ -87,20 +89,20 @@ describe('Test explore links', () => {
const newChartName = `Test chart [${shortid.generate()}]`; const newChartName = `Test chart [${shortid.generate()}]`;
cy.visitChartByParams(JSON.stringify(formData)); cy.visitChartByParams(JSON.stringify(formData));
cy.verifySliceSuccess({ waitAlias: '@postJson' }); cy.verifySliceSuccess({ waitAlias: '@tableChartData' });
cy.url().then(() => { cy.url().then(() => {
cy.get('[data-test="query-save-button"]').click(); cy.get('[data-test="query-save-button"]').click();
cy.get('[data-test="saveas-radio"]').check(); cy.get('[data-test="saveas-radio"]').check();
cy.get('[data-test="new-chart-name"]').type(newChartName); cy.get('[data-test="new-chart-name"]').type(newChartName);
cy.get('[data-test="btn-modal-save"]').click(); cy.get('[data-test="btn-modal-save"]').click();
cy.verifySliceSuccess({ waitAlias: '@postJson' }); cy.verifySliceSuccess({ waitAlias: '@tableChartData' });
cy.visitChartByName(newChartName); cy.visitChartByName(newChartName);
// Overwriting! // Overwriting!
cy.get('[data-test="query-save-button"]').click(); cy.get('[data-test="query-save-button"]').click();
cy.get('[data-test="save-overwrite-radio"]').check(); cy.get('[data-test="save-overwrite-radio"]').check();
cy.get('[data-test="btn-modal-save"]').click(); cy.get('[data-test="btn-modal-save"]').click();
cy.verifySliceSuccess({ waitAlias: '@postJson' }); cy.verifySliceSuccess({ waitAlias: '@tableChartData' });
const query = { const query = {
filters: [ filters: [
{ {
@ -110,6 +112,7 @@ describe('Test explore links', () => {
}, },
], ],
}; };
cy.request(apiURL('/api/v1/chart/', query)).then(response => { cy.request(apiURL('/api/v1/chart/', query)).then(response => {
expect(response.body.count).equals(1); expect(response.body.count).equals(1);
cy.request('DELETE', `/api/v1/chart/${response.body.ids[0]}`); cy.request('DELETE', `/api/v1/chart/${response.body.ids[0]}`);
@ -123,7 +126,7 @@ describe('Test explore links', () => {
const dashboardTitle = `Test dashboard [${shortid.generate()}]`; const dashboardTitle = `Test dashboard [${shortid.generate()}]`;
cy.visitChartByName(chartName); cy.visitChartByName(chartName);
cy.verifySliceSuccess({ waitAlias: '@postJson' }); cy.verifySliceSuccess({ waitAlias: '@chartData' });
cy.get('[data-test="query-save-button"]').click(); cy.get('[data-test="query-save-button"]').click();
cy.get('[data-test="saveas-radio"]').check(); cy.get('[data-test="saveas-radio"]').check();
@ -134,7 +137,7 @@ describe('Test explore links', () => {
.type(`${dashboardTitle}{enter}{enter}`); .type(`${dashboardTitle}{enter}{enter}`);
cy.get('[data-test="btn-modal-save"]').click(); cy.get('[data-test="btn-modal-save"]').click();
cy.verifySliceSuccess({ waitAlias: '@postJson' }); cy.verifySliceSuccess({ waitAlias: '@chartData' });
let query = { let query = {
filters: [ filters: [
{ {
@ -149,7 +152,7 @@ describe('Test explore links', () => {
}); });
cy.visitChartByName(newChartName); cy.visitChartByName(newChartName);
cy.verifySliceSuccess({ waitAlias: '@postJson' }); cy.verifySliceSuccess({ waitAlias: '@chartData' });
cy.get('[data-test="query-save-button"]').click(); cy.get('[data-test="query-save-button"]').click();
cy.get('[data-test="save-overwrite-radio"]').check(); cy.get('[data-test="save-overwrite-radio"]').check();
@ -161,7 +164,7 @@ describe('Test explore links', () => {
.type(`${dashboardTitle}{enter}{enter}`); .type(`${dashboardTitle}{enter}{enter}`);
cy.get('[data-test="btn-modal-save"]').click(); cy.get('[data-test="btn-modal-save"]').click();
cy.verifySliceSuccess({ waitAlias: '@postJson' }); cy.verifySliceSuccess({ waitAlias: '@chartData' });
query = { query = {
filters: [ filters: [
{ {

View File

@ -16,6 +16,7 @@
* specific language governing permissions and limitations * specific language governing permissions and limitations
* under the License. * under the License.
*/ */
import { interceptChart } from 'cypress/utils';
import { import {
FORM_DATA_DEFAULTS, FORM_DATA_DEFAULTS,
NUM_METRIC, NUM_METRIC,
@ -34,7 +35,7 @@ describe('Visualization > Table', () => {
const PERCENT_METRIC = { const PERCENT_METRIC = {
expressionType: 'SQL', expressionType: 'SQL',
sqlExpression: 'CAST(SUM(num_girls)+AS+FLOAT)/SUM(num)', sqlExpression: 'CAST(SUM(num_girls) AS FLOAT)/SUM(num)',
column: null, column: null,
aggregate: null, aggregate: null,
hasCustomLabel: true, hasCustomLabel: true,
@ -44,7 +45,7 @@ describe('Visualization > Table', () => {
beforeEach(() => { beforeEach(() => {
cy.login(); cy.login();
cy.intercept('POST', '/superset/explore_json/**').as('getJson'); interceptChart({ legacy: false }).as('chartData');
}); });
it('Use default time column', () => { it('Use default time column', () => {
@ -66,8 +67,8 @@ describe('Visualization > Table', () => {
}); });
// when format with smart_date, time column use format by granularity // when format with smart_date, time column use format by granularity
cy.get('.chart-container td:nth-child(1)').contains('2008 Q1'); cy.get('.chart-container td:nth-child(1)').contains('2008 Q1');
// other column with timestamp use raw timestamp // other column with timestamp use adaptive formatting
cy.get('.chart-container td:nth-child(3)').contains('2008-01-01T00:00:00'); cy.get('.chart-container td:nth-child(3)').contains('2008');
cy.get('.chart-container td:nth-child(4)').contains('TX'); cy.get('.chart-container td:nth-child(4)').contains('TX');
}); });
@ -99,7 +100,7 @@ describe('Visualization > Table', () => {
groupby: ['name'], groupby: ['name'],
}); });
cy.verifySliceSuccess({ cy.verifySliceSuccess({
waitAlias: '@getJson', waitAlias: '@chartData',
querySubstring: /group by.*name/i, querySubstring: /group by.*name/i,
chartSelector: 'table', chartSelector: 'table',
}); });
@ -115,14 +116,14 @@ describe('Visualization > Table', () => {
groupby: ['name'], groupby: ['name'],
}); });
cy.verifySliceSuccess({ cy.verifySliceSuccess({
waitAlias: '@getJson', waitAlias: '@chartData',
querySubstring: /group by.*name/i, querySubstring: /group by.*name/i,
chartSelector: 'table', chartSelector: 'table',
}); });
// should handle sorting correctly // should handle sorting correctly
cy.get('.chart-container th').contains('name').click(); cy.get('.chart-container th').contains('name').click();
cy.get('.chart-container td:nth-child(2):eq(0)').contains('Abigail'); cy.get('.chart-container td:nth-child(2):eq(0)').contains('Aaron');
cy.get('.chart-container th').contains('Time').click().click(); cy.get('.chart-container th').contains('Time').click().click();
cy.get('.chart-container td:nth-child(1):eq(0)').contains('2008'); cy.get('.chart-container td:nth-child(1):eq(0)').contains('2008');
}); });
@ -134,7 +135,7 @@ describe('Visualization > Table', () => {
metrics: [], metrics: [],
groupby: ['name'], groupby: ['name'],
}); });
cy.verifySliceSuccess({ waitAlias: '@getJson', chartSelector: 'table' }); cy.verifySliceSuccess({ waitAlias: '@chartData', chartSelector: 'table' });
}); });
it('Test table with groupby order desc', () => { it('Test table with groupby order desc', () => {
@ -144,7 +145,7 @@ describe('Visualization > Table', () => {
groupby: ['name'], groupby: ['name'],
order_desc: true, order_desc: true,
}); });
cy.verifySliceSuccess({ waitAlias: '@getJson', chartSelector: 'table' }); cy.verifySliceSuccess({ waitAlias: '@chartData', chartSelector: 'table' });
}); });
it('Test table with groupby and limit', () => { it('Test table with groupby and limit', () => {
@ -156,9 +157,9 @@ describe('Visualization > Table', () => {
row_limit: limit, row_limit: limit,
}; };
cy.visitChartByParams(JSON.stringify(formData)); cy.visitChartByParams(JSON.stringify(formData));
cy.wait('@getJson').then(({ response }) => { cy.wait('@chartData').then(({ response }) => {
cy.verifySliceContainer('table'); cy.verifySliceContainer('table');
expect(response?.body.data.records.length).to.eq(limit); expect(response?.body.result[0].data.length).to.eq(limit);
}); });
cy.get('span.label-danger').contains('10 rows'); cy.get('span.label-danger').contains('10 rows');
}); });
@ -178,7 +179,7 @@ describe('Visualization > Table', () => {
cy.get('div[data-test="all_columns"]').should('be.visible'); cy.get('div[data-test="all_columns"]').should('be.visible');
cy.get('div[data-test="groupby"]').should('not.exist'); cy.get('div[data-test="groupby"]').should('not.exist');
cy.verifySliceSuccess({ waitAlias: '@getJson', chartSelector: 'table' }); cy.verifySliceSuccess({ waitAlias: '@chartData', chartSelector: 'table' });
// should allow switch to aggregate mode // should allow switch to aggregate mode
cy.get('div[data-test="query_mode"] .btn').contains('Aggregate').click(); cy.get('div[data-test="query_mode"] .btn').contains('Aggregate').click();
@ -196,14 +197,13 @@ describe('Visualization > Table', () => {
all_columns: ['name', 'state', 'ds', 'num'], all_columns: ['name', 'state', 'ds', 'num'],
metrics: [], metrics: [],
row_limit: limit, row_limit: limit,
order_by_cols: ['["num",+false]'], order_by_cols: ['["num", false]'],
}; };
cy.visitChartByParams(JSON.stringify(formData)); cy.visitChartByParams(JSON.stringify(formData));
cy.wait('@getJson').then(({ response }) => { cy.wait('@chartData').then(({ response }) => {
cy.verifySliceContainer('table'); cy.verifySliceContainer('table');
const responseBody = response?.body; const records = response?.body.result[0].data;
const { records } = responseBody.data;
expect(records[0].num).greaterThan(records[records.length - 1].num); expect(records[0].num).greaterThan(records[records.length - 1].num);
}); });
}); });
@ -215,7 +215,7 @@ describe('Visualization > Table', () => {
const formData = { ...VIZ_DEFAULTS, metrics, adhoc_filters: filters }; const formData = { ...VIZ_DEFAULTS, metrics, adhoc_filters: filters };
cy.visitChartByParams(JSON.stringify(formData)); cy.visitChartByParams(JSON.stringify(formData));
cy.verifySliceSuccess({ waitAlias: '@getJson', chartSelector: 'table' }); cy.verifySliceSuccess({ waitAlias: '@chartData', chartSelector: 'table' });
}); });
it('Tests table number formatting with % in metric name', () => { it('Tests table number formatting with % in metric name', () => {
@ -227,7 +227,7 @@ describe('Visualization > Table', () => {
cy.visitChartByParams(JSON.stringify(formData)); cy.visitChartByParams(JSON.stringify(formData));
cy.verifySliceSuccess({ cy.verifySliceSuccess({
waitAlias: '@getJson', waitAlias: '@chartData',
querySubstring: /group by.*state/i, querySubstring: /group by.*state/i,
chartSelector: 'table', chartSelector: 'table',
}); });

View File

@ -90,9 +90,8 @@ Cypress.Commands.add(
cy.verifySliceContainer(chartSelector); cy.verifySliceContainer(chartSelector);
const responseBody = response?.body; const responseBody = response?.body;
if (querySubstring) { if (querySubstring) {
const query = responseBody const query: string =
? (responseBody as { query: string }).query responseBody.query || responseBody.result[0].query || '';
: '';
if (querySubstring instanceof RegExp) { if (querySubstring instanceof RegExp) {
expect(query).to.match(querySubstring); expect(query).to.match(querySubstring);
} else { } else {

View File

@ -17,7 +17,30 @@
* under the License. * under the License.
*/ */
const V1_PLUGINS = ['box_plot', 'echarts_timeseries', 'word_cloud', 'pie']; export type JsonPrimitive = string | number | boolean | null;
export type JsonValue = JsonPrimitive | JsonObject | JsonArray;
export type JsonArray = JsonValue[];
// eslint-disable-next-line @typescript-eslint/no-explicit-any
export type JsonObject = { [member: string]: any };
export interface Slice {
slice_id: number;
form_data: {
viz_type: string;
};
}
export interface Dashboard {
slices: Slice[];
}
const V1_PLUGINS = [
'box_plot',
'echarts_timeseries',
'word_cloud',
'pie',
'table',
];
export const DASHBOARD_CHART_ALIAS_PREFIX = 'getJson_'; export const DASHBOARD_CHART_ALIAS_PREFIX = 'getJson_';
export function isLegacyChart(vizType: string): boolean { export function isLegacyChart(vizType: string): boolean {
@ -34,7 +57,7 @@ export function getSliceIdFromRequestUrl(url: string) {
return query?.match(/\d+/)?.[0]; return query?.match(/\d+/)?.[0];
} }
export function getChartAliases(slices: any[]): string[] { export function getChartAliases(slices: Slice[]): string[] {
const aliases: string[] = []; const aliases: string[] = [];
Array.from(slices).forEach(slice => { Array.from(slices).forEach(slice => {
const vizType = slice.form_data.viz_type; const vizType = slice.form_data.viz_type;
@ -54,11 +77,24 @@ export function getChartAliases(slices: any[]): string[] {
return aliases; return aliases;
} }
export function interceptChart(sliceId: number, isLegacy = true) { export function interceptChart({
const formData = { slice_id: sliceId }; sliceId,
const encodedFormData = encodeURIComponent(JSON.stringify(formData)); legacy = false,
const url = isLegacy method = 'POST',
? `**/superset/explore_json/?form_data=${encodedFormData}*` }: {
: `**/api/v1/chart/data?form_data=${encodedFormData}*`; sliceId?: number;
return cy.intercept('POST', url); legacy?: boolean;
method?: 'POST' | 'GET';
}) {
const urlBase = legacy ? '**/superset/explore_json/' : '**/api/v1/chart/data';
let url;
if (sliceId) {
const encodedFormData = encodeURIComponent(
JSON.stringify({ slice_id: sliceId }),
);
url = `${urlBase}?form_data=${encodedFormData}*`;
} else {
url = `${urlBase}**`;
}
return cy.intercept(method, url);
} }

View File

@ -6072,9 +6072,12 @@
} }
}, },
"d3-array": { "d3-array": {
"version": "2.9.1", "version": "2.11.0",
"resolved": "https://registry.npmjs.org/d3-array/-/d3-array-2.9.1.tgz", "resolved": "https://registry.npmjs.org/d3-array/-/d3-array-2.11.0.tgz",
"integrity": "sha512-Ob7RdOtkqsjx1NWyQHMFLtCSk6/aKTxDdC4ZIolX+O+mDD2RzrsYgAyc0WGAlfYFVELLSilS7w8BtE3PKM8bHg==" "integrity": "sha512-26clcwmHQEdsLv34oNKq5Ia9tQ26Y/4HqS3dQzF42QBUqymZJ+9PORcN1G52bt37NsL2ABoX4lvyYZc+A9Y0zw==",
"requires": {
"internmap": "^1.0.0"
}
}, },
"d3-interpolate": { "d3-interpolate": {
"version": "2.0.1", "version": "2.0.1",
@ -18549,19 +18552,19 @@
} }
}, },
"@superset-ui/chart-controls": { "@superset-ui/chart-controls": {
"version": "0.16.9", "version": "0.17.1",
"resolved": "https://registry.npmjs.org/@superset-ui/chart-controls/-/chart-controls-0.16.9.tgz", "resolved": "https://registry.npmjs.org/@superset-ui/chart-controls/-/chart-controls-0.17.1.tgz",
"integrity": "sha512-GTwnJx5AhiYqwed3F3FCz+8Yuc56jlLM/g872zoHYQUejnAXGs/Iomeznga6+281DKfsbCO6ptH6qiOZYDH8PA==", "integrity": "sha512-dfJRoVH0WbG5FQ8smszVtiYLI3NvvLAQxW6HRgOqTLegiKocIIB8hjpMpGrOPxx2G0mBigAubeQowC1VOlpAZQ==",
"requires": { "requires": {
"@superset-ui/core": "0.16.7", "@superset-ui/core": "0.17.1",
"lodash": "^4.17.15", "lodash": "^4.17.15",
"prop-types": "^15.7.2" "prop-types": "^15.7.2"
} }
}, },
"@superset-ui/core": { "@superset-ui/core": {
"version": "0.16.7", "version": "0.17.1",
"resolved": "https://registry.npmjs.org/@superset-ui/core/-/core-0.16.7.tgz", "resolved": "https://registry.npmjs.org/@superset-ui/core/-/core-0.17.1.tgz",
"integrity": "sha512-9i/o9ZC+dJibhoWZnoKGvxMMFcz65LjHuYk+hRspuRWA4qwobcdu64piQpfwuFVhw1yh3cbdxq+OSsNmNX9A9g==", "integrity": "sha512-VnWhb5FjMOrAF2+PJG4WkvscmgtRnnFZBEqG+2g8TSSby2RfIrGB390Dq6abqc9SmBmjNPLj6zkzsvJZv8DsOA==",
"requires": { "requires": {
"@babel/runtime": "^7.1.2", "@babel/runtime": "^7.1.2",
"@emotion/core": "^10.0.28", "@emotion/core": "^10.0.28",
@ -18574,7 +18577,7 @@
"@types/lodash": "^4.14.149", "@types/lodash": "^4.14.149",
"@types/rison": "0.0.6", "@types/rison": "0.0.6",
"@types/seedrandom": "^2.4.28", "@types/seedrandom": "^2.4.28",
"@vx/responsive": "^0.0.197", "@vx/responsive": "^0.0.199",
"csstype": "^2.6.4", "csstype": "^2.6.4",
"d3-format": "^1.3.2", "d3-format": "^1.3.2",
"d3-interpolate": "^1.4.0", "d3-interpolate": "^1.4.0",
@ -18594,9 +18597,9 @@
}, },
"dependencies": { "dependencies": {
"@vx/responsive": { "@vx/responsive": {
"version": "0.0.197", "version": "0.0.199",
"resolved": "https://registry.npmjs.org/@vx/responsive/-/responsive-0.0.197.tgz", "resolved": "https://registry.npmjs.org/@vx/responsive/-/responsive-0.0.199.tgz",
"integrity": "sha512-Qv15PJ/Hy79LjyfJ/9E8z+zacKAnD43O2Jg9wvB6PFSNs73xPEDy/mHTYxH+FZv94ruAE3scBO0330W29sQpyg==", "integrity": "sha512-ONrmLUAG+8wzD3cn/EmsuZh6JHeyejqup3ZsV25t04VaVJAVQAJukAfNdH8YiwSJu0zSo+txkBTfrnOmFyQLOw==",
"requires": { "requires": {
"@types/lodash": "^4.14.146", "@types/lodash": "^4.14.146",
"@types/react": "*", "@types/react": "*",
@ -18606,9 +18609,12 @@
} }
}, },
"d3-array": { "d3-array": {
"version": "2.9.1", "version": "2.11.0",
"resolved": "https://registry.npmjs.org/d3-array/-/d3-array-2.9.1.tgz", "resolved": "https://registry.npmjs.org/d3-array/-/d3-array-2.11.0.tgz",
"integrity": "sha512-Ob7RdOtkqsjx1NWyQHMFLtCSk6/aKTxDdC4ZIolX+O+mDD2RzrsYgAyc0WGAlfYFVELLSilS7w8BtE3PKM8bHg==" "integrity": "sha512-26clcwmHQEdsLv34oNKq5Ia9tQ26Y/4HqS3dQzF42QBUqymZJ+9PORcN1G52bt37NsL2ABoX4lvyYZc+A9Y0zw==",
"requires": {
"internmap": "^1.0.0"
}
}, },
"d3-interpolate": { "d3-interpolate": {
"version": "1.4.0", "version": "1.4.0",
@ -18641,12 +18647,12 @@
} }
}, },
"@superset-ui/legacy-plugin-chart-calendar": { "@superset-ui/legacy-plugin-chart-calendar": {
"version": "0.16.9", "version": "0.17.1",
"resolved": "https://registry.npmjs.org/@superset-ui/legacy-plugin-chart-calendar/-/legacy-plugin-chart-calendar-0.16.9.tgz", "resolved": "https://registry.npmjs.org/@superset-ui/legacy-plugin-chart-calendar/-/legacy-plugin-chart-calendar-0.17.1.tgz",
"integrity": "sha512-8SbUVhuyXYB4Jh4ucFEonhWwF9/rEVOmR/E28bfS3dVJ3lChRx2T8ijv0pRxXNuPSl619Qm61/ec4637V7I//g==", "integrity": "sha512-v9Hh2hNdxsu3vSRtx1KjqsDhDYlCStbEQekp2+BKRH55RKitbJzbw+6GgXxA09s/6VgIxji8oEaZVYAWylzJtQ==",
"requires": { "requires": {
"@superset-ui/chart-controls": "0.16.9", "@superset-ui/chart-controls": "0.17.1",
"@superset-ui/core": "0.16.7", "@superset-ui/core": "0.17.1",
"d3-array": "^2.0.3", "d3-array": "^2.0.3",
"d3-selection": "^1.4.0", "d3-selection": "^1.4.0",
"d3-tip": "^0.9.1", "d3-tip": "^0.9.1",
@ -18654,72 +18660,78 @@
}, },
"dependencies": { "dependencies": {
"d3-array": { "d3-array": {
"version": "2.9.1", "version": "2.11.0",
"resolved": "https://registry.npmjs.org/d3-array/-/d3-array-2.9.1.tgz", "resolved": "https://registry.npmjs.org/d3-array/-/d3-array-2.11.0.tgz",
"integrity": "sha512-Ob7RdOtkqsjx1NWyQHMFLtCSk6/aKTxDdC4ZIolX+O+mDD2RzrsYgAyc0WGAlfYFVELLSilS7w8BtE3PKM8bHg==" "integrity": "sha512-26clcwmHQEdsLv34oNKq5Ia9tQ26Y/4HqS3dQzF42QBUqymZJ+9PORcN1G52bt37NsL2ABoX4lvyYZc+A9Y0zw==",
"requires": {
"internmap": "^1.0.0"
}
} }
} }
}, },
"@superset-ui/legacy-plugin-chart-chord": { "@superset-ui/legacy-plugin-chart-chord": {
"version": "0.16.9", "version": "0.17.1",
"resolved": "https://registry.npmjs.org/@superset-ui/legacy-plugin-chart-chord/-/legacy-plugin-chart-chord-0.16.9.tgz", "resolved": "https://registry.npmjs.org/@superset-ui/legacy-plugin-chart-chord/-/legacy-plugin-chart-chord-0.17.1.tgz",
"integrity": "sha512-QK3yJLDzkrIDYAfvAXmZI/nms0fl54WPSQlEN42e17IRe8Z/UNMd7Un4IyAEPjNJqt53uBJq5CEKIBAIB2eTng==", "integrity": "sha512-xJbr9oyHBOBRp1IWQn1HuLsrArJtADVk2FE6r4QZTVYCKzJoKrQTqQEfkA2roHOHfOZtlcHcD1rlOMt8KpdmDw==",
"requires": { "requires": {
"@superset-ui/chart-controls": "0.16.9", "@superset-ui/chart-controls": "0.17.1",
"@superset-ui/core": "0.16.7", "@superset-ui/core": "0.17.1",
"d3": "^3.5.17", "d3": "^3.5.17",
"prop-types": "^15.6.2", "prop-types": "^15.6.2",
"react": "^16.13.1" "react": "^16.13.1"
} }
}, },
"@superset-ui/legacy-plugin-chart-country-map": { "@superset-ui/legacy-plugin-chart-country-map": {
"version": "0.16.9", "version": "0.17.1",
"resolved": "https://registry.npmjs.org/@superset-ui/legacy-plugin-chart-country-map/-/legacy-plugin-chart-country-map-0.16.9.tgz", "resolved": "https://registry.npmjs.org/@superset-ui/legacy-plugin-chart-country-map/-/legacy-plugin-chart-country-map-0.17.1.tgz",
"integrity": "sha512-h58JI45Gg/Y7FQEY3v6Wsh07XvYSrH88d+6MJN0Iv72nv9X2iC5h9QVyN0M0jYDpqnfMkoPzsP90Ana8pYqjCw==", "integrity": "sha512-CCJPFGp0P1lEX4W0JqcSC0Lq43gx8BSUNeE//xz+ZKc9JoERttVCKjwEyFCov6Y++iFM/EfDpg1bRS0XpJxD4A==",
"requires": { "requires": {
"@superset-ui/chart-controls": "0.16.9", "@superset-ui/chart-controls": "0.17.1",
"@superset-ui/core": "0.16.7", "@superset-ui/core": "0.17.1",
"d3": "^3.5.17", "d3": "^3.5.17",
"d3-array": "^2.0.3", "d3-array": "^2.0.3",
"prop-types": "^15.6.2" "prop-types": "^15.6.2"
}, },
"dependencies": { "dependencies": {
"d3-array": { "d3-array": {
"version": "2.9.1", "version": "2.11.0",
"resolved": "https://registry.npmjs.org/d3-array/-/d3-array-2.9.1.tgz", "resolved": "https://registry.npmjs.org/d3-array/-/d3-array-2.11.0.tgz",
"integrity": "sha512-Ob7RdOtkqsjx1NWyQHMFLtCSk6/aKTxDdC4ZIolX+O+mDD2RzrsYgAyc0WGAlfYFVELLSilS7w8BtE3PKM8bHg==" "integrity": "sha512-26clcwmHQEdsLv34oNKq5Ia9tQ26Y/4HqS3dQzF42QBUqymZJ+9PORcN1G52bt37NsL2ABoX4lvyYZc+A9Y0zw==",
"requires": {
"internmap": "^1.0.0"
}
} }
} }
}, },
"@superset-ui/legacy-plugin-chart-event-flow": { "@superset-ui/legacy-plugin-chart-event-flow": {
"version": "0.16.9", "version": "0.17.1",
"resolved": "https://registry.npmjs.org/@superset-ui/legacy-plugin-chart-event-flow/-/legacy-plugin-chart-event-flow-0.16.9.tgz", "resolved": "https://registry.npmjs.org/@superset-ui/legacy-plugin-chart-event-flow/-/legacy-plugin-chart-event-flow-0.17.1.tgz",
"integrity": "sha512-FWaaXmZXaslb0XlJS3xRcZlFpvGcWtNlGc4x01jrW8vSUzZ16wnQqqPO7b46K6LDJYfgaglXEzNK4gwPWLHKoA==", "integrity": "sha512-pGuo5cVjLRJILKbE2oc7mFoWUTrOIf2ChNLHpULZZeNtpG8H3gXGA97qK+5KgXtslfv2BVi1sbR97VV9IH3gTw==",
"requires": { "requires": {
"@data-ui/event-flow": "^0.0.84", "@data-ui/event-flow": "^0.0.84",
"@superset-ui/chart-controls": "0.16.9", "@superset-ui/chart-controls": "0.17.1",
"@superset-ui/core": "0.16.7", "@superset-ui/core": "0.17.1",
"prop-types": "^15.6.2" "prop-types": "^15.6.2"
} }
}, },
"@superset-ui/legacy-plugin-chart-force-directed": { "@superset-ui/legacy-plugin-chart-force-directed": {
"version": "0.16.9", "version": "0.17.1",
"resolved": "https://registry.npmjs.org/@superset-ui/legacy-plugin-chart-force-directed/-/legacy-plugin-chart-force-directed-0.16.9.tgz", "resolved": "https://registry.npmjs.org/@superset-ui/legacy-plugin-chart-force-directed/-/legacy-plugin-chart-force-directed-0.17.1.tgz",
"integrity": "sha512-2kryYHT1HqzpYLXkMW5ocCVEJ57K1zGII6mP+piIhqkEgzfSUL1+mUjfzUypuvc+zpKOuWiDxaGwVJqE8gGSbA==", "integrity": "sha512-F8aV/iGBeHOh+9ewE8rfpWN2J/avAvlWl1zIM/PZTMlwimVwBXj8voTWk32LVL+Cb+9DwntB5KA093r8yWHK5Q==",
"requires": { "requires": {
"@superset-ui/chart-controls": "0.16.9", "@superset-ui/chart-controls": "0.17.1",
"@superset-ui/core": "0.16.7", "@superset-ui/core": "0.17.1",
"d3": "^3.5.17", "d3": "^3.5.17",
"prop-types": "^15.7.2" "prop-types": "^15.7.2"
} }
}, },
"@superset-ui/legacy-plugin-chart-heatmap": { "@superset-ui/legacy-plugin-chart-heatmap": {
"version": "0.16.9", "version": "0.17.1",
"resolved": "https://registry.npmjs.org/@superset-ui/legacy-plugin-chart-heatmap/-/legacy-plugin-chart-heatmap-0.16.9.tgz", "resolved": "https://registry.npmjs.org/@superset-ui/legacy-plugin-chart-heatmap/-/legacy-plugin-chart-heatmap-0.17.1.tgz",
"integrity": "sha512-sw1/gOzAyZq6ki7ZocM9KH6BfYuT/yO2zfxW8OMB/8HeMVrM2gPkljSgIX6GYwY5Y83g3ZI+dVPXcp0HNTR0yQ==", "integrity": "sha512-IlItjyVT9Y3aE3qYml+CEbGpwVrPJu68MYb5UNOp+ms5DEETRH0Z61kKvX/egCVouYznyVrxjdc8SvL8tHqQYg==",
"requires": { "requires": {
"@superset-ui/chart-controls": "0.16.9", "@superset-ui/chart-controls": "0.17.1",
"@superset-ui/core": "0.16.7", "@superset-ui/core": "0.17.1",
"d3": "^3.5.17", "d3": "^3.5.17",
"d3-svg-legend": "^1.x", "d3-svg-legend": "^1.x",
"d3-tip": "^0.9.1", "d3-tip": "^0.9.1",
@ -18727,16 +18739,16 @@
} }
}, },
"@superset-ui/legacy-plugin-chart-histogram": { "@superset-ui/legacy-plugin-chart-histogram": {
"version": "0.16.9", "version": "0.17.1",
"resolved": "https://registry.npmjs.org/@superset-ui/legacy-plugin-chart-histogram/-/legacy-plugin-chart-histogram-0.16.9.tgz", "resolved": "https://registry.npmjs.org/@superset-ui/legacy-plugin-chart-histogram/-/legacy-plugin-chart-histogram-0.17.1.tgz",
"integrity": "sha512-8g4NXTxRjojIntF/ovDoXKaDws/cg6G4CHtll3fLmr85eXSvJATtYODF+KsjefBmGmfIXNUr0kdIPAjfyprizA==", "integrity": "sha512-awZCVmpXH5LCOOCktZdaj5PrPUBeAWdK/60A6n/oKufZ2x4eoXVLgBpsj3JCS73XcKu3FwhWRHLujE0pZthKhA==",
"requires": { "requires": {
"@data-ui/histogram": "^0.0.84", "@data-ui/histogram": "^0.0.84",
"@data-ui/theme": "^0.0.84", "@data-ui/theme": "^0.0.84",
"@superset-ui/chart-controls": "0.16.9", "@superset-ui/chart-controls": "0.17.1",
"@superset-ui/core": "0.16.7", "@superset-ui/core": "0.17.1",
"@vx/legend": "^0.0.198", "@vx/legend": "^0.0.198",
"@vx/responsive": "^0.0.197", "@vx/responsive": "^0.0.199",
"@vx/scale": "^0.0.197", "@vx/scale": "^0.0.197",
"prop-types": "^15.6.2" "prop-types": "^15.6.2"
}, },
@ -18766,9 +18778,9 @@
} }
}, },
"@vx/responsive": { "@vx/responsive": {
"version": "0.0.197", "version": "0.0.199",
"resolved": "https://registry.npmjs.org/@vx/responsive/-/responsive-0.0.197.tgz", "resolved": "https://registry.npmjs.org/@vx/responsive/-/responsive-0.0.199.tgz",
"integrity": "sha512-Qv15PJ/Hy79LjyfJ/9E8z+zacKAnD43O2Jg9wvB6PFSNs73xPEDy/mHTYxH+FZv94ruAE3scBO0330W29sQpyg==", "integrity": "sha512-ONrmLUAG+8wzD3cn/EmsuZh6JHeyejqup3ZsV25t04VaVJAVQAJukAfNdH8YiwSJu0zSo+txkBTfrnOmFyQLOw==",
"requires": { "requires": {
"@types/lodash": "^4.14.146", "@types/lodash": "^4.14.146",
"@types/react": "*", "@types/react": "*",
@ -18802,21 +18814,24 @@
} }
}, },
"@superset-ui/legacy-plugin-chart-horizon": { "@superset-ui/legacy-plugin-chart-horizon": {
"version": "0.16.9", "version": "0.17.1",
"resolved": "https://registry.npmjs.org/@superset-ui/legacy-plugin-chart-horizon/-/legacy-plugin-chart-horizon-0.16.9.tgz", "resolved": "https://registry.npmjs.org/@superset-ui/legacy-plugin-chart-horizon/-/legacy-plugin-chart-horizon-0.17.1.tgz",
"integrity": "sha512-l5uCO0w1cCcMeI5lJtwhOLZO1509Fj+OCZuvsRCe/pjMncwH8tAwxVljGZ54YLLIWi9Dqo1WFQvA2BLsjQcv6w==", "integrity": "sha512-1omJPgUSktLCqBXqDMMqb9dFfflxWGmEdl6lxVPMqqCrlRILdNCd1rdsQFsu1cN90FmZav7AMVj7SLvIHWvNnQ==",
"requires": { "requires": {
"@superset-ui/chart-controls": "0.16.9", "@superset-ui/chart-controls": "0.17.1",
"@superset-ui/core": "0.16.7", "@superset-ui/core": "0.17.1",
"d3-array": "^2.0.3", "d3-array": "^2.0.3",
"d3-scale": "^3.0.1", "d3-scale": "^3.0.1",
"prop-types": "^15.6.2" "prop-types": "^15.6.2"
}, },
"dependencies": { "dependencies": {
"d3-array": { "d3-array": {
"version": "2.9.1", "version": "2.11.0",
"resolved": "https://registry.npmjs.org/d3-array/-/d3-array-2.9.1.tgz", "resolved": "https://registry.npmjs.org/d3-array/-/d3-array-2.11.0.tgz",
"integrity": "sha512-Ob7RdOtkqsjx1NWyQHMFLtCSk6/aKTxDdC4ZIolX+O+mDD2RzrsYgAyc0WGAlfYFVELLSilS7w8BtE3PKM8bHg==" "integrity": "sha512-26clcwmHQEdsLv34oNKq5Ia9tQ26Y/4HqS3dQzF42QBUqymZJ+9PORcN1G52bt37NsL2ABoX4lvyYZc+A9Y0zw==",
"requires": {
"internmap": "^1.0.0"
}
}, },
"d3-scale": { "d3-scale": {
"version": "3.2.3", "version": "3.2.3",
@ -18833,12 +18848,12 @@
} }
}, },
"@superset-ui/legacy-plugin-chart-map-box": { "@superset-ui/legacy-plugin-chart-map-box": {
"version": "0.16.9", "version": "0.17.1",
"resolved": "https://registry.npmjs.org/@superset-ui/legacy-plugin-chart-map-box/-/legacy-plugin-chart-map-box-0.16.9.tgz", "resolved": "https://registry.npmjs.org/@superset-ui/legacy-plugin-chart-map-box/-/legacy-plugin-chart-map-box-0.17.1.tgz",
"integrity": "sha512-cf7QdN64rAo4UAmzirhHcJMO7IBYvMX+Slu7/LsCX4CnrVmJtO4d+vGFlNS5FaWU+t2A6lVH5QDpSI1WBgcfAA==", "integrity": "sha512-RHI9k3ulGodGjKgX2kBF3muMyTZKCQGPXV6BbNRzV8DJCc6eGrcE1eznC0ipHiy4yBYeKHfMwcWX3jh9dCI/kg==",
"requires": { "requires": {
"@superset-ui/chart-controls": "0.16.9", "@superset-ui/chart-controls": "0.17.1",
"@superset-ui/core": "0.16.7", "@superset-ui/core": "0.17.1",
"immutable": "^3.8.2", "immutable": "^3.8.2",
"mapbox-gl": "^0.53.0", "mapbox-gl": "^0.53.0",
"prop-types": "^15.6.2", "prop-types": "^15.6.2",
@ -18855,118 +18870,118 @@
} }
}, },
"@superset-ui/legacy-plugin-chart-paired-t-test": { "@superset-ui/legacy-plugin-chart-paired-t-test": {
"version": "0.16.9", "version": "0.17.1",
"resolved": "https://registry.npmjs.org/@superset-ui/legacy-plugin-chart-paired-t-test/-/legacy-plugin-chart-paired-t-test-0.16.9.tgz", "resolved": "https://registry.npmjs.org/@superset-ui/legacy-plugin-chart-paired-t-test/-/legacy-plugin-chart-paired-t-test-0.17.1.tgz",
"integrity": "sha512-YEVJtqn25SWLr0N33BHzm5CjAOuSkwkOrxl05Lmceyf2z2PwHhrCnnLWXRzEEszQ9IXTsbKT8HYJtPkKNFVIWw==", "integrity": "sha512-WExiHSMvByu8+weNJoKll82cPrhF4zNRnMGzdiYMewJ6TZLN4Ws3ZLR+b2c26BnWQFyA8qXPpsCcQzX9c9WODw==",
"requires": { "requires": {
"@superset-ui/chart-controls": "0.16.9", "@superset-ui/chart-controls": "0.17.1",
"@superset-ui/core": "0.16.7", "@superset-ui/core": "0.17.1",
"distributions": "^1.0.0", "distributions": "^1.0.0",
"prop-types": "^15.6.2", "prop-types": "^15.6.2",
"reactable-arc": "0.15.0" "reactable-arc": "0.15.0"
} }
}, },
"@superset-ui/legacy-plugin-chart-parallel-coordinates": { "@superset-ui/legacy-plugin-chart-parallel-coordinates": {
"version": "0.16.9", "version": "0.17.1",
"resolved": "https://registry.npmjs.org/@superset-ui/legacy-plugin-chart-parallel-coordinates/-/legacy-plugin-chart-parallel-coordinates-0.16.9.tgz", "resolved": "https://registry.npmjs.org/@superset-ui/legacy-plugin-chart-parallel-coordinates/-/legacy-plugin-chart-parallel-coordinates-0.17.1.tgz",
"integrity": "sha512-zX6uToyBEV5fKh+cpELDQ+xD9c+TtAGqKsc7r1mpY/R2Vfzw7oD5aOrwcxBshtMoJxx72jo9EuC5nzgNQf4+ug==", "integrity": "sha512-v6HPEyQRKEOhVpydKzVo+HWUDNaYJhgWL/mrr/kDhT+htUPOUqtTqQZ2BsvgpTQiX4qXHUMnAOXAN7eBa4Xf3w==",
"requires": { "requires": {
"@superset-ui/chart-controls": "0.16.9", "@superset-ui/chart-controls": "0.17.1",
"@superset-ui/core": "0.16.7", "@superset-ui/core": "0.17.1",
"d3": "^3.5.17", "d3": "^3.5.17",
"prop-types": "^15.7.2" "prop-types": "^15.7.2"
} }
}, },
"@superset-ui/legacy-plugin-chart-partition": { "@superset-ui/legacy-plugin-chart-partition": {
"version": "0.16.9", "version": "0.17.1",
"resolved": "https://registry.npmjs.org/@superset-ui/legacy-plugin-chart-partition/-/legacy-plugin-chart-partition-0.16.9.tgz", "resolved": "https://registry.npmjs.org/@superset-ui/legacy-plugin-chart-partition/-/legacy-plugin-chart-partition-0.17.1.tgz",
"integrity": "sha512-OEmirurMTPAInqMEjGkMQqJDUsM0TMJ4OCFaJ2vtwbvlwk3Fgar/fm/m/qhaLzDLXfTIgb6TyUkxiBCWmmF0iA==", "integrity": "sha512-Zx7lEjgz0N/MQBmXFrhAsjryIl7QzZc5Gi5bXEi9GYiXcDUaZJmLW0cUnJT5Maqgwt3HCtKgCDZEh/SRj+XBsQ==",
"requires": { "requires": {
"@superset-ui/chart-controls": "0.16.9", "@superset-ui/chart-controls": "0.17.1",
"@superset-ui/core": "0.16.7", "@superset-ui/core": "0.17.1",
"d3": "^3.5.17", "d3": "^3.5.17",
"d3-hierarchy": "^1.1.8", "d3-hierarchy": "^1.1.8",
"prop-types": "^15.6.2" "prop-types": "^15.6.2"
} }
}, },
"@superset-ui/legacy-plugin-chart-pivot-table": { "@superset-ui/legacy-plugin-chart-pivot-table": {
"version": "0.16.9", "version": "0.17.1",
"resolved": "https://registry.npmjs.org/@superset-ui/legacy-plugin-chart-pivot-table/-/legacy-plugin-chart-pivot-table-0.16.9.tgz", "resolved": "https://registry.npmjs.org/@superset-ui/legacy-plugin-chart-pivot-table/-/legacy-plugin-chart-pivot-table-0.17.1.tgz",
"integrity": "sha512-gpEXyC/WlBVeRD2INPx1IfOi3QwmPkr9bdwLfhXe34xU620uj85jO8VKlSU+tGppiyZz6aNaC2WBU4nTRRXUgg==", "integrity": "sha512-qlXdtaKNQsMibpyJIeQUqaJTLE4Uce9Kn47t9mgB6lCLQc5c+egM9hVwB57P8tUxMBCJePQcjiRIJRnyLKjOTA==",
"requires": { "requires": {
"@superset-ui/chart-controls": "0.16.9", "@superset-ui/chart-controls": "0.17.1",
"@superset-ui/core": "0.16.7", "@superset-ui/core": "0.17.1",
"d3": "^3.5.17", "d3": "^3.5.17",
"datatables.net-bs": "^1.10.15", "datatables.net-bs": "^1.10.15",
"prop-types": "^15.6.2" "prop-types": "^15.6.2"
} }
}, },
"@superset-ui/legacy-plugin-chart-rose": { "@superset-ui/legacy-plugin-chart-rose": {
"version": "0.16.9", "version": "0.17.1",
"resolved": "https://registry.npmjs.org/@superset-ui/legacy-plugin-chart-rose/-/legacy-plugin-chart-rose-0.16.9.tgz", "resolved": "https://registry.npmjs.org/@superset-ui/legacy-plugin-chart-rose/-/legacy-plugin-chart-rose-0.17.1.tgz",
"integrity": "sha512-T6amU5vtlyjohI065OBmLktNW4aNmVsh8UAxSOzKJBRyVDSk0cs1Ntb6FG6jNZLxe7AYh0YVmnYeLI9IVLs2VA==", "integrity": "sha512-dQykgdgrtOTAq4ck/uZ98R8Vhv4UtercXUlNDMTsiJo5nz3/Ka/4Oz3zBf3Pey+3JrsIOQtJP1vKRKt/j37Zkg==",
"requires": { "requires": {
"@superset-ui/chart-controls": "0.16.9", "@superset-ui/chart-controls": "0.17.1",
"@superset-ui/core": "0.16.7", "@superset-ui/core": "0.17.1",
"d3": "^3.5.17", "d3": "^3.5.17",
"nvd3": "1.8.6", "nvd3": "1.8.6",
"prop-types": "^15.6.2" "prop-types": "^15.6.2"
} }
}, },
"@superset-ui/legacy-plugin-chart-sankey": { "@superset-ui/legacy-plugin-chart-sankey": {
"version": "0.16.9", "version": "0.17.1",
"resolved": "https://registry.npmjs.org/@superset-ui/legacy-plugin-chart-sankey/-/legacy-plugin-chart-sankey-0.16.9.tgz", "resolved": "https://registry.npmjs.org/@superset-ui/legacy-plugin-chart-sankey/-/legacy-plugin-chart-sankey-0.17.1.tgz",
"integrity": "sha512-Vkauo64wsuRMznDtpqHWPrP6vohnm119wUlWPYk2y9/0e1PgowlRyv6X0RUqWyk2z4/eUXIzGj5YUebqniYjtA==", "integrity": "sha512-gy5COpeeEHllYFuenXwdZr5OZ8bL1g+C16bGbjACG9/kIaF9ShoTN+Ad5cNTepb78Cox+WhmPHKIzbgPybN3Bw==",
"requires": { "requires": {
"@superset-ui/chart-controls": "0.16.9", "@superset-ui/chart-controls": "0.17.1",
"@superset-ui/core": "0.16.7", "@superset-ui/core": "0.17.1",
"d3": "^3.5.17", "d3": "^3.5.17",
"d3-sankey": "^0.4.2", "d3-sankey": "^0.4.2",
"prop-types": "^15.6.2" "prop-types": "^15.6.2"
} }
}, },
"@superset-ui/legacy-plugin-chart-sankey-loop": { "@superset-ui/legacy-plugin-chart-sankey-loop": {
"version": "0.16.9", "version": "0.17.1",
"resolved": "https://registry.npmjs.org/@superset-ui/legacy-plugin-chart-sankey-loop/-/legacy-plugin-chart-sankey-loop-0.16.9.tgz", "resolved": "https://registry.npmjs.org/@superset-ui/legacy-plugin-chart-sankey-loop/-/legacy-plugin-chart-sankey-loop-0.17.1.tgz",
"integrity": "sha512-LtnVsvnoNTrHrOoZcdCuzYX/akCsaiMvWG53KoQis9dsu9+vc4xfKySw0Dct/QpGU+904YbGfFX9qDzh+RCsyw==", "integrity": "sha512-r46LqceZi1hv6DDiZMinuU/hR+jyAgURcBvXy/5GvEAF+0eVHUzqbQ8SWivmFCNRwVTAEBMYkUa3IKZ6LBeUbw==",
"requires": { "requires": {
"@superset-ui/chart-controls": "0.16.9", "@superset-ui/chart-controls": "0.17.1",
"@superset-ui/core": "0.16.7", "@superset-ui/core": "0.17.1",
"d3-sankey-diagram": "^0.7.3", "d3-sankey-diagram": "^0.7.3",
"d3-selection": "^1.4.0", "d3-selection": "^1.4.0",
"prop-types": "^15.6.2" "prop-types": "^15.6.2"
} }
}, },
"@superset-ui/legacy-plugin-chart-sunburst": { "@superset-ui/legacy-plugin-chart-sunburst": {
"version": "0.16.9", "version": "0.17.1",
"resolved": "https://registry.npmjs.org/@superset-ui/legacy-plugin-chart-sunburst/-/legacy-plugin-chart-sunburst-0.16.9.tgz", "resolved": "https://registry.npmjs.org/@superset-ui/legacy-plugin-chart-sunburst/-/legacy-plugin-chart-sunburst-0.17.1.tgz",
"integrity": "sha512-DTOuXy7fPUvwUM8WjiKGmbydcIynAbXlEGdmi/ObLlh4lACaTPCX8IOGc18eJP0G78pxeXpByK9JoEZuCbQyEA==", "integrity": "sha512-hyP36lNaLBWCKfRXTMTG/uvJZa1bMjyX0jYDRAY/tNVxPBAjhTrB76SW1eShfpugg6baaqys9XrbVkxiKRcFnA==",
"requires": { "requires": {
"@superset-ui/chart-controls": "0.16.9", "@superset-ui/chart-controls": "0.17.1",
"@superset-ui/core": "0.16.7", "@superset-ui/core": "0.17.1",
"d3": "^3.5.17", "d3": "^3.5.17",
"prop-types": "^15.6.2" "prop-types": "^15.6.2"
} }
}, },
"@superset-ui/legacy-plugin-chart-treemap": { "@superset-ui/legacy-plugin-chart-treemap": {
"version": "0.16.9", "version": "0.17.1",
"resolved": "https://registry.npmjs.org/@superset-ui/legacy-plugin-chart-treemap/-/legacy-plugin-chart-treemap-0.16.9.tgz", "resolved": "https://registry.npmjs.org/@superset-ui/legacy-plugin-chart-treemap/-/legacy-plugin-chart-treemap-0.17.1.tgz",
"integrity": "sha512-P+IQYjaMrmN2RqmIEJ2V3w8UQG6jxLPGgmsK/KkGMCzl8PAJJtPcRZw2Z/9JGh8u/7B70JCAEL+sRcLQCA/aTQ==", "integrity": "sha512-z6dQo1ZDb2/drZUJ3nYScBbDXHiIYchtpscYNszyB/jGxJ90ridUniLQicyLxMkjdK4mpuBW9MgidSg0y0I0sw==",
"requires": { "requires": {
"@superset-ui/chart-controls": "0.16.9", "@superset-ui/chart-controls": "0.17.1",
"@superset-ui/core": "0.16.7", "@superset-ui/core": "0.17.1",
"d3-hierarchy": "^1.1.8", "d3-hierarchy": "^1.1.8",
"d3-selection": "^1.4.0", "d3-selection": "^1.4.0",
"prop-types": "^15.6.2" "prop-types": "^15.6.2"
} }
}, },
"@superset-ui/legacy-plugin-chart-world-map": { "@superset-ui/legacy-plugin-chart-world-map": {
"version": "0.16.9", "version": "0.17.1",
"resolved": "https://registry.npmjs.org/@superset-ui/legacy-plugin-chart-world-map/-/legacy-plugin-chart-world-map-0.16.9.tgz", "resolved": "https://registry.npmjs.org/@superset-ui/legacy-plugin-chart-world-map/-/legacy-plugin-chart-world-map-0.17.1.tgz",
"integrity": "sha512-PtYzSKgiaInMt5NYyAmG4b6acffYvN1W8hetpq8TpeMkhuLzA69D3rsAj72ky/dgy5/n14t8wgdfKyfrrt9c6A==", "integrity": "sha512-S9XuCVUIbgfCH4sG0PWGMdEc14fzunZIiUZOC2hh2JtLV/vU452XO4a9viTzWFvAY75zHNetgkTtuoCsrQdLOw==",
"requires": { "requires": {
"@superset-ui/chart-controls": "0.16.9", "@superset-ui/chart-controls": "0.17.1",
"@superset-ui/core": "0.16.7", "@superset-ui/core": "0.17.1",
"d3": "^3.5.17", "d3": "^3.5.17",
"d3-array": "^2.4.0", "d3-array": "^2.4.0",
"d3-color": "^1.4.1", "d3-color": "^1.4.1",
@ -18975,9 +18990,12 @@
}, },
"dependencies": { "dependencies": {
"d3-array": { "d3-array": {
"version": "2.9.1", "version": "2.11.0",
"resolved": "https://registry.npmjs.org/d3-array/-/d3-array-2.9.1.tgz", "resolved": "https://registry.npmjs.org/d3-array/-/d3-array-2.11.0.tgz",
"integrity": "sha512-Ob7RdOtkqsjx1NWyQHMFLtCSk6/aKTxDdC4ZIolX+O+mDD2RzrsYgAyc0WGAlfYFVELLSilS7w8BtE3PKM8bHg==" "integrity": "sha512-26clcwmHQEdsLv34oNKq5Ia9tQ26Y/4HqS3dQzF42QBUqymZJ+9PORcN1G52bt37NsL2ABoX4lvyYZc+A9Y0zw==",
"requires": {
"internmap": "^1.0.0"
}
}, },
"d3-color": { "d3-color": {
"version": "1.4.1", "version": "1.4.1",
@ -18987,13 +19005,13 @@
} }
}, },
"@superset-ui/legacy-preset-chart-big-number": { "@superset-ui/legacy-preset-chart-big-number": {
"version": "0.16.9", "version": "0.17.1",
"resolved": "https://registry.npmjs.org/@superset-ui/legacy-preset-chart-big-number/-/legacy-preset-chart-big-number-0.16.9.tgz", "resolved": "https://registry.npmjs.org/@superset-ui/legacy-preset-chart-big-number/-/legacy-preset-chart-big-number-0.17.1.tgz",
"integrity": "sha512-Iby3rNcdv+cVy1A/I/Lg/n76v71JdPxKJJc+A4YUil3SalmFFo4mxm0glrX9dex6IYxpZMWbDJKgnHEbhalXlw==", "integrity": "sha512-961i+DqTNcPNlvH3GAB2ofViEk4CcZFdcjZ/rMdCzlEichmLLrNzKUPfouvhMpMokb4ysEADOkQvE7POlKjDWw==",
"requires": { "requires": {
"@data-ui/xy-chart": "^0.0.84", "@data-ui/xy-chart": "^0.0.84",
"@superset-ui/chart-controls": "0.16.9", "@superset-ui/chart-controls": "0.17.1",
"@superset-ui/core": "0.16.7", "@superset-ui/core": "0.17.1",
"@types/d3-color": "^1.2.2", "@types/d3-color": "^1.2.2",
"@types/shortid": "^0.0.29", "@types/shortid": "^0.0.29",
"d3-color": "^1.2.3", "d3-color": "^1.2.3",
@ -19043,34 +19061,128 @@
"nvd3-fork": "^2.0.5", "nvd3-fork": "^2.0.5",
"prop-types": "^15.6.2", "prop-types": "^15.6.2",
"urijs": "^1.18.10" "urijs": "^1.18.10"
},
"dependencies": {
"@superset-ui/chart-controls": {
"version": "0.16.9",
"resolved": "https://registry.npmjs.org/@superset-ui/chart-controls/-/chart-controls-0.16.9.tgz",
"integrity": "sha512-GTwnJx5AhiYqwed3F3FCz+8Yuc56jlLM/g872zoHYQUejnAXGs/Iomeznga6+281DKfsbCO6ptH6qiOZYDH8PA==",
"requires": {
"@superset-ui/core": "0.16.7",
"lodash": "^4.17.15",
"prop-types": "^15.7.2"
}
},
"@superset-ui/core": {
"version": "0.16.7",
"resolved": "https://registry.npmjs.org/@superset-ui/core/-/core-0.16.7.tgz",
"integrity": "sha512-9i/o9ZC+dJibhoWZnoKGvxMMFcz65LjHuYk+hRspuRWA4qwobcdu64piQpfwuFVhw1yh3cbdxq+OSsNmNX9A9g==",
"requires": {
"@babel/runtime": "^7.1.2",
"@emotion/core": "^10.0.28",
"@emotion/styled": "^10.0.27",
"@types/d3-format": "^1.3.0",
"@types/d3-interpolate": "^1.3.1",
"@types/d3-scale": "^2.1.1",
"@types/d3-time": "^1.0.9",
"@types/d3-time-format": "^2.1.0",
"@types/lodash": "^4.14.149",
"@types/rison": "0.0.6",
"@types/seedrandom": "^2.4.28",
"@vx/responsive": "^0.0.197",
"csstype": "^2.6.4",
"d3-format": "^1.3.2",
"d3-interpolate": "^1.4.0",
"d3-scale": "^3.0.0",
"d3-time": "^1.0.10",
"d3-time-format": "^2.2.0",
"emotion-theming": "^10.0.27",
"fetch-retry": "^4.0.1",
"jed": "^1.1.1",
"lodash": "^4.17.11",
"pretty-ms": "^7.0.0",
"react-error-boundary": "^1.2.5",
"reselect": "^4.0.0",
"rison": "^0.1.1",
"seedrandom": "^3.0.5",
"whatwg-fetch": "^3.0.0"
}
},
"@vx/responsive": {
"version": "0.0.197",
"resolved": "https://registry.npmjs.org/@vx/responsive/-/responsive-0.0.197.tgz",
"integrity": "sha512-Qv15PJ/Hy79LjyfJ/9E8z+zacKAnD43O2Jg9wvB6PFSNs73xPEDy/mHTYxH+FZv94ruAE3scBO0330W29sQpyg==",
"requires": {
"@types/lodash": "^4.14.146",
"@types/react": "*",
"lodash": "^4.17.10",
"prop-types": "^15.6.1",
"resize-observer-polyfill": "1.5.1"
}
},
"d3-array": {
"version": "2.11.0",
"resolved": "https://registry.npmjs.org/d3-array/-/d3-array-2.11.0.tgz",
"integrity": "sha512-26clcwmHQEdsLv34oNKq5Ia9tQ26Y/4HqS3dQzF42QBUqymZJ+9PORcN1G52bt37NsL2ABoX4lvyYZc+A9Y0zw==",
"requires": {
"internmap": "^1.0.0"
}
},
"d3-interpolate": {
"version": "1.4.0",
"resolved": "https://registry.npmjs.org/d3-interpolate/-/d3-interpolate-1.4.0.tgz",
"integrity": "sha512-V9znK0zc3jOPV4VD2zZn0sDhZU3WAE2bmlxdIwwQPPzPjvyLkd8B3JUVdS1IDUFDkWZ72c9qnv1GK2ZagTZ8EA==",
"requires": {
"d3-color": "1"
}
},
"d3-scale": {
"version": "3.2.3",
"resolved": "https://registry.npmjs.org/d3-scale/-/d3-scale-3.2.3.tgz",
"integrity": "sha512-8E37oWEmEzj57bHcnjPVOBS3n4jqakOeuv1EDdQSiSrYnMCBdMd3nc4HtKk7uia8DUHcY/CGuJ42xxgtEYrX0g==",
"requires": {
"d3-array": "^2.3.0",
"d3-format": "1 - 2",
"d3-interpolate": "1.2.0 - 2",
"d3-time": "1 - 2",
"d3-time-format": "2 - 3"
}
},
"d3-time-format": {
"version": "2.3.0",
"resolved": "https://registry.npmjs.org/d3-time-format/-/d3-time-format-2.3.0.tgz",
"integrity": "sha512-guv6b2H37s2Uq/GefleCDtbe0XZAuy7Wa49VGkPVPMfLL9qObgBST3lEHJBMUp8S7NdLQAGIvr2KXk8Hc98iKQ==",
"requires": {
"d3-time": "1"
}
}
} }
}, },
"@superset-ui/plugin-chart-echarts": { "@superset-ui/plugin-chart-echarts": {
"version": "0.16.9", "version": "0.17.1",
"resolved": "https://registry.npmjs.org/@superset-ui/plugin-chart-echarts/-/plugin-chart-echarts-0.16.9.tgz", "resolved": "https://registry.npmjs.org/@superset-ui/plugin-chart-echarts/-/plugin-chart-echarts-0.17.1.tgz",
"integrity": "sha512-MC1eEq3BLedRR+tL88BnisSUkqBxAi4t79JuGY2q9Lgg+7yh1wgD6bqKkR8JaH3SdqSK2XpG0dPkjHZXqFpkMQ==", "integrity": "sha512-+PzvoEbeTfrZ+SkM/bldwGljTjy+VBSz/AoPsDEgKmaJ8UZSG7rXQM01X0so7XkD9WRvAMI5q6+uFjL2zfDJlw==",
"requires": { "requires": {
"@superset-ui/chart-controls": "0.16.9", "@superset-ui/chart-controls": "0.17.1",
"@superset-ui/core": "0.16.7", "@superset-ui/core": "0.17.1",
"@types/echarts": "^4.6.3", "@types/echarts": "^4.9.3",
"@types/mathjs": "^6.0.7", "@types/mathjs": "^6.0.7",
"echarts": "^5.0.0", "echarts": "^5.0.0",
"mathjs": "^8.0.1" "mathjs": "^8.0.1"
} }
}, },
"@superset-ui/plugin-chart-table": { "@superset-ui/plugin-chart-table": {
"version": "0.16.9", "version": "0.17.1",
"resolved": "https://registry.npmjs.org/@superset-ui/plugin-chart-table/-/plugin-chart-table-0.16.9.tgz", "resolved": "https://registry.npmjs.org/@superset-ui/plugin-chart-table/-/plugin-chart-table-0.17.1.tgz",
"integrity": "sha512-SA6ypwoJq1A9nG/xaI+xRhSRG34omNakgNv3vgeBqCYuZLHwSCf+a/c7liwAA9PhlSTNB7CuOZHC542SVSe6ZQ==", "integrity": "sha512-Jkf4nAU2usJUQthRMO5dSdeatlUvI+3QYCWujneTXCbR4MMBXhnlePlD0LO5dTteLNBql56rg2SbD10NDIZGZA==",
"requires": { "requires": {
"@emotion/core": "^10.0.28", "@emotion/core": "^10.0.28",
"@superset-ui/chart-controls": "0.16.9", "@superset-ui/chart-controls": "0.17.1",
"@superset-ui/core": "0.16.7", "@superset-ui/core": "0.17.1",
"@types/d3-array": "^2.0.0", "@types/d3-array": "^2.0.0",
"@types/match-sorter": "^4.0.0",
"@types/react-table": "^7.0.19", "@types/react-table": "^7.0.19",
"d3-array": "^2.4.0", "d3-array": "^2.4.0",
"match-sorter": "^4.1.0", "match-sorter": "^6.1.0",
"memoize-one": "^5.1.1", "memoize-one": "^5.1.1",
"react-icons": "^3.10.0", "react-icons": "^3.10.0",
"react-table": "^7.2.1", "react-table": "^7.2.1",
@ -19078,37 +19190,23 @@
"xss": "^1.0.6" "xss": "^1.0.6"
}, },
"dependencies": { "dependencies": {
"@babel/runtime": {
"version": "7.12.5",
"resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.12.5.tgz",
"integrity": "sha512-plcc+hbExy3McchJCEQG3knOsuh3HH+Prx1P6cLIkET/0dLuQDEnrT+s27Axgc9bqfsmNUNHfscgMUdBpC9xfg==",
"requires": {
"regenerator-runtime": "^0.13.4"
}
},
"d3-array": { "d3-array": {
"version": "2.9.1", "version": "2.11.0",
"resolved": "https://registry.npmjs.org/d3-array/-/d3-array-2.9.1.tgz", "resolved": "https://registry.npmjs.org/d3-array/-/d3-array-2.11.0.tgz",
"integrity": "sha512-Ob7RdOtkqsjx1NWyQHMFLtCSk6/aKTxDdC4ZIolX+O+mDD2RzrsYgAyc0WGAlfYFVELLSilS7w8BtE3PKM8bHg==" "integrity": "sha512-26clcwmHQEdsLv34oNKq5Ia9tQ26Y/4HqS3dQzF42QBUqymZJ+9PORcN1G52bt37NsL2ABoX4lvyYZc+A9Y0zw==",
},
"match-sorter": {
"version": "4.2.1",
"resolved": "https://registry.npmjs.org/match-sorter/-/match-sorter-4.2.1.tgz",
"integrity": "sha512-s+3h9TiZU9U1pWhIERHf8/f4LmBN6IXaRgo2CI17+XGByGS1GvG5VvXK9pcGyCjGe3WM3mSYRC3ipGrd5UEVgw==",
"requires": { "requires": {
"@babel/runtime": "^7.10.5", "internmap": "^1.0.0"
"remove-accents": "0.4.2"
} }
} }
} }
}, },
"@superset-ui/plugin-chart-word-cloud": { "@superset-ui/plugin-chart-word-cloud": {
"version": "0.16.9", "version": "0.17.1",
"resolved": "https://registry.npmjs.org/@superset-ui/plugin-chart-word-cloud/-/plugin-chart-word-cloud-0.16.9.tgz", "resolved": "https://registry.npmjs.org/@superset-ui/plugin-chart-word-cloud/-/plugin-chart-word-cloud-0.17.1.tgz",
"integrity": "sha512-NWbPAqxxF8V16xH3jxhhJu9HAoU9BWRZktvhKePQeAtFrGZQ79nQzU+tHYivWvVmjn2bcfhg3D5V5rJwysN9Uw==", "integrity": "sha512-jPz/22L3IwIoQqsHEFqwQTGyYdatednazPB3zGUv1KMzkj4AyU/sd5AsnCCDDC7EL10ylhy9NB8EYk12x5Z7vw==",
"requires": { "requires": {
"@superset-ui/chart-controls": "0.16.9", "@superset-ui/chart-controls": "0.17.1",
"@superset-ui/core": "0.16.7", "@superset-ui/core": "0.17.1",
"@types/d3-cloud": "^1.2.1", "@types/d3-cloud": "^1.2.1",
"@types/d3-scale": "^2.0.2", "@types/d3-scale": "^2.0.2",
"d3-cloud": "^1.2.5", "d3-cloud": "^1.2.5",
@ -19118,9 +19216,12 @@
}, },
"dependencies": { "dependencies": {
"d3-array": { "d3-array": {
"version": "2.9.1", "version": "2.11.0",
"resolved": "https://registry.npmjs.org/d3-array/-/d3-array-2.9.1.tgz", "resolved": "https://registry.npmjs.org/d3-array/-/d3-array-2.11.0.tgz",
"integrity": "sha512-Ob7RdOtkqsjx1NWyQHMFLtCSk6/aKTxDdC4ZIolX+O+mDD2RzrsYgAyc0WGAlfYFVELLSilS7w8BtE3PKM8bHg==" "integrity": "sha512-26clcwmHQEdsLv34oNKq5Ia9tQ26Y/4HqS3dQzF42QBUqymZJ+9PORcN1G52bt37NsL2ABoX4lvyYZc+A9Y0zw==",
"requires": {
"internmap": "^1.0.0"
}
}, },
"d3-scale": { "d3-scale": {
"version": "3.2.3", "version": "3.2.3",
@ -19137,14 +19238,14 @@
} }
}, },
"@superset-ui/preset-chart-xy": { "@superset-ui/preset-chart-xy": {
"version": "0.16.9", "version": "0.17.1",
"resolved": "https://registry.npmjs.org/@superset-ui/preset-chart-xy/-/preset-chart-xy-0.16.9.tgz", "resolved": "https://registry.npmjs.org/@superset-ui/preset-chart-xy/-/preset-chart-xy-0.17.1.tgz",
"integrity": "sha512-hWIuTVcpVINXAlCSEDKKRWiAatx/d15pFmMaaZC7NzpQJ2XfZC6jpCmwgSxbvhbezbduQhwU7DViNINbg2XoZg==", "integrity": "sha512-N1mSF8OE04n+xM8Fh6ZsNLD1ARGnVlh3zzld1YmhasS7rRP8UZ3STGEjLm4IV8mTYeVc+i7+Xg/VI5Fl42Uhow==",
"requires": { "requires": {
"@data-ui/theme": "^0.0.84", "@data-ui/theme": "^0.0.84",
"@data-ui/xy-chart": "^0.0.84", "@data-ui/xy-chart": "^0.0.84",
"@superset-ui/chart-controls": "0.16.9", "@superset-ui/chart-controls": "0.17.1",
"@superset-ui/core": "0.16.7", "@superset-ui/core": "0.17.1",
"@vx/axis": "^0.0.198", "@vx/axis": "^0.0.198",
"@vx/legend": "^0.0.198", "@vx/legend": "^0.0.198",
"@vx/scale": "^0.0.197", "@vx/scale": "^0.0.197",
@ -21002,11 +21103,6 @@
"@types/react": "*" "@types/react": "*"
} }
}, },
"@types/match-sorter": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/@types/match-sorter/-/match-sorter-4.0.0.tgz",
"integrity": "sha512-JK7HNHXZA7i/nEp6fbNAxoX/1j1ysZXmv2/nlkt2UpX1LiUWKLtyt/dMmDTlMPR6t6PkwMmIr2W2AAyu6oELNw=="
},
"@types/mathjs": { "@types/mathjs": {
"version": "6.0.11", "version": "6.0.11",
"resolved": "https://registry.npmjs.org/@types/mathjs/-/mathjs-6.0.11.tgz", "resolved": "https://registry.npmjs.org/@types/mathjs/-/mathjs-6.0.11.tgz",
@ -21194,6 +21290,16 @@
"@types/react": "*" "@types/react": "*"
} }
}, },
"@types/react-loadable": {
"version": "5.5.4",
"resolved": "https://registry.npmjs.org/@types/react-loadable/-/react-loadable-5.5.4.tgz",
"integrity": "sha512-otKcjNCfVUzdBMdwOqFITTmBruIXw6GeoZitTBvJ6BMrif8Utu2JLy42GWukNnYI7ewJdncUCooz5Y/1dBz4+w==",
"dev": true,
"requires": {
"@types/react": "*",
"@types/webpack": "*"
}
},
"@types/react-redux": { "@types/react-redux": {
"version": "7.1.10", "version": "7.1.10",
"resolved": "https://registry.npmjs.org/@types/react-redux/-/react-redux-7.1.10.tgz", "resolved": "https://registry.npmjs.org/@types/react-redux/-/react-redux-7.1.10.tgz",
@ -28683,9 +28789,12 @@
}, },
"dependencies": { "dependencies": {
"d3-array": { "d3-array": {
"version": "2.9.1", "version": "2.11.0",
"resolved": "https://registry.npmjs.org/d3-array/-/d3-array-2.9.1.tgz", "resolved": "https://registry.npmjs.org/d3-array/-/d3-array-2.11.0.tgz",
"integrity": "sha512-Ob7RdOtkqsjx1NWyQHMFLtCSk6/aKTxDdC4ZIolX+O+mDD2RzrsYgAyc0WGAlfYFVELLSilS7w8BtE3PKM8bHg==" "integrity": "sha512-26clcwmHQEdsLv34oNKq5Ia9tQ26Y/4HqS3dQzF42QBUqymZJ+9PORcN1G52bt37NsL2ABoX4lvyYZc+A9Y0zw==",
"requires": {
"internmap": "^1.0.0"
}
}, },
"d3-interpolate": { "d3-interpolate": {
"version": "2.0.1", "version": "2.0.1",
@ -33267,6 +33376,11 @@
} }
} }
}, },
"internmap": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/internmap/-/internmap-1.0.0.tgz",
"integrity": "sha512-SdoDWwNOTE2n4JWUsLn4KXZGuZPjPF9yyOGc8bnfWnBQh7BD/l80rzSznKc/r4Y0aQ7z3RTk9X+tV4tHBpu+dA=="
},
"interpret": { "interpret": {
"version": "1.1.0", "version": "1.1.0",
"resolved": "https://registry.npmjs.org/interpret/-/interpret-1.1.0.tgz", "resolved": "https://registry.npmjs.org/interpret/-/interpret-1.1.0.tgz",

View File

@ -65,34 +65,34 @@
"@babel/runtime-corejs3": "^7.12.5", "@babel/runtime-corejs3": "^7.12.5",
"@data-ui/sparkline": "^0.0.84", "@data-ui/sparkline": "^0.0.84",
"@emotion/core": "^10.0.35", "@emotion/core": "^10.0.35",
"@superset-ui/chart-controls": "^0.16.9", "@superset-ui/chart-controls": "^0.17.1",
"@superset-ui/core": "^0.16.7", "@superset-ui/core": "^0.17.1",
"@superset-ui/legacy-plugin-chart-calendar": "^0.16.9", "@superset-ui/legacy-plugin-chart-calendar": "^0.17.1",
"@superset-ui/legacy-plugin-chart-chord": "^0.16.9", "@superset-ui/legacy-plugin-chart-chord": "^0.17.1",
"@superset-ui/legacy-plugin-chart-country-map": "^0.16.9", "@superset-ui/legacy-plugin-chart-country-map": "^0.17.1",
"@superset-ui/legacy-plugin-chart-event-flow": "^0.16.9", "@superset-ui/legacy-plugin-chart-event-flow": "^0.17.1",
"@superset-ui/legacy-plugin-chart-force-directed": "^0.16.9", "@superset-ui/legacy-plugin-chart-force-directed": "^0.17.1",
"@superset-ui/legacy-plugin-chart-heatmap": "^0.16.9", "@superset-ui/legacy-plugin-chart-heatmap": "^0.17.1",
"@superset-ui/legacy-plugin-chart-histogram": "^0.16.9", "@superset-ui/legacy-plugin-chart-histogram": "^0.17.1",
"@superset-ui/legacy-plugin-chart-horizon": "^0.16.9", "@superset-ui/legacy-plugin-chart-horizon": "^0.17.1",
"@superset-ui/legacy-plugin-chart-map-box": "^0.16.9", "@superset-ui/legacy-plugin-chart-map-box": "^0.17.1",
"@superset-ui/legacy-plugin-chart-paired-t-test": "^0.16.9", "@superset-ui/legacy-plugin-chart-paired-t-test": "^0.17.1",
"@superset-ui/legacy-plugin-chart-parallel-coordinates": "^0.16.9", "@superset-ui/legacy-plugin-chart-parallel-coordinates": "^0.17.1",
"@superset-ui/legacy-plugin-chart-partition": "^0.16.9", "@superset-ui/legacy-plugin-chart-partition": "^0.17.1",
"@superset-ui/legacy-plugin-chart-pivot-table": "^0.16.9", "@superset-ui/legacy-plugin-chart-pivot-table": "^0.17.1",
"@superset-ui/legacy-plugin-chart-rose": "^0.16.9", "@superset-ui/legacy-plugin-chart-rose": "^0.17.1",
"@superset-ui/legacy-plugin-chart-sankey": "^0.16.9", "@superset-ui/legacy-plugin-chart-sankey": "^0.17.1",
"@superset-ui/legacy-plugin-chart-sankey-loop": "^0.16.9", "@superset-ui/legacy-plugin-chart-sankey-loop": "^0.17.1",
"@superset-ui/legacy-plugin-chart-sunburst": "^0.16.9", "@superset-ui/legacy-plugin-chart-sunburst": "^0.17.1",
"@superset-ui/legacy-plugin-chart-treemap": "^0.16.9", "@superset-ui/legacy-plugin-chart-treemap": "^0.17.1",
"@superset-ui/legacy-plugin-chart-world-map": "^0.16.9", "@superset-ui/legacy-plugin-chart-world-map": "^0.17.1",
"@superset-ui/legacy-preset-chart-big-number": "^0.16.9", "@superset-ui/legacy-preset-chart-big-number": "^0.17.1",
"@superset-ui/legacy-preset-chart-deckgl": "^0.4.1", "@superset-ui/legacy-preset-chart-deckgl": "^0.4.1",
"@superset-ui/legacy-preset-chart-nvd3": "^0.16.10", "@superset-ui/legacy-preset-chart-nvd3": "^0.16.10",
"@superset-ui/plugin-chart-echarts": "^0.16.9", "@superset-ui/plugin-chart-echarts": "^0.17.1",
"@superset-ui/plugin-chart-table": "^0.16.9", "@superset-ui/plugin-chart-table": "^0.17.1",
"@superset-ui/plugin-chart-word-cloud": "^0.16.9", "@superset-ui/plugin-chart-word-cloud": "^0.17.1",
"@superset-ui/preset-chart-xy": "^0.16.9", "@superset-ui/preset-chart-xy": "^0.17.1",
"@vx/responsive": "^0.0.195", "@vx/responsive": "^0.0.195",
"abortcontroller-polyfill": "^1.1.9", "abortcontroller-polyfill": "^1.1.9",
"antd": "^4.9.4", "antd": "^4.9.4",
@ -214,6 +214,7 @@
"@types/react-dom": "^16.9.8", "@types/react-dom": "^16.9.8",
"@types/react-gravatar": "^2.6.8", "@types/react-gravatar": "^2.6.8",
"@types/react-json-tree": "^0.6.11", "@types/react-json-tree": "^0.6.11",
"@types/react-loadable": "^5.5.4",
"@types/react-redux": "^7.1.10", "@types/react-redux": "^7.1.10",
"@types/react-router-dom": "^5.1.5", "@types/react-router-dom": "^5.1.5",
"@types/react-select": "^3.0.19", "@types/react-select": "^3.0.19",

View File

@ -27,7 +27,7 @@
], ],
// for supressing errors caused by incompatible @types/react when `npm link` // for supressing errors caused by incompatible @types/react when `npm link`
// Ref: https://github.com/Microsoft/typescript/issues/6496#issuecomment-384786222 // Ref: https://github.com/Microsoft/typescript/issues/6496#issuecomment-384786222
"react": ["./node_modules/@types/react"] "react": ["./node_modules/@types/react", "react"]
}, },
"skipLibCheck": true, "skipLibCheck": true,
"sourceMap": true, "sourceMap": true,

View File

@ -65,7 +65,7 @@ from superset.charts.schemas import (
from superset.commands.exceptions import CommandInvalidError from superset.commands.exceptions import CommandInvalidError
from superset.commands.importers.v1.utils import get_contents_from_bundle from superset.commands.importers.v1.utils import get_contents_from_bundle
from superset.constants import MODEL_API_RW_METHOD_PERMISSION_MAP, RouteMethod from superset.constants import MODEL_API_RW_METHOD_PERMISSION_MAP, RouteMethod
from superset.exceptions import SupersetSecurityException from superset.exceptions import QueryObjectValidationError, SupersetSecurityException
from superset.extensions import event_logger from superset.extensions import event_logger
from superset.models.slice import Slice from superset.models.slice import Slice
from superset.tasks.thumbnails import cache_chart_thumbnail from superset.tasks.thumbnails import cache_chart_thumbnail
@ -566,9 +566,13 @@ class ChartRestApi(BaseSupersetModelRestApi):
command = ChartDataCommand() command = ChartDataCommand()
query_context = command.set_query_context(json_body) query_context = command.set_query_context(json_body)
command.validate() command.validate()
except QueryObjectValidationError as error:
return self.response_400(message=error.message)
except ValidationError as error: except ValidationError as error:
return self.response_400( return self.response_400(
message=_("Request is incorrect: %(error)s", error=error.messages) message=_(
"Request is incorrect: %(error)s", error=error.normalized_messages()
)
) )
except SupersetSecurityException: except SupersetSecurityException:
return self.response_401() return self.response_401()

View File

@ -22,7 +22,7 @@ from typing import Any, cast, ClassVar, Dict, List, Optional, Union
import numpy as np import numpy as np
import pandas as pd import pandas as pd
from flask_babel import gettext as _ from flask_babel import _
from superset import app, db, is_feature_enabled from superset import app, db, is_feature_enabled
from superset.annotation_layers.dao import AnnotationLayerDAO from superset.annotation_layers.dao import AnnotationLayerDAO
@ -142,7 +142,9 @@ class QueryContext:
"""Converting metrics to numeric when pandas.read_sql cannot""" """Converting metrics to numeric when pandas.read_sql cannot"""
for col, dtype in df.dtypes.items(): for col, dtype in df.dtypes.items():
if dtype.type == np.object_ and col in query_object.metric_names: if dtype.type == np.object_ and col in query_object.metric_names:
df[col] = pd.to_numeric(df[col], errors="coerce") # soft-convert a metric column to numeric
# will stay as strings if conversion fails
df[col] = df[col].infer_objects()
def get_data(self, df: pd.DataFrame,) -> Union[str, List[Dict[str, Any]]]: def get_data(self, df: pd.DataFrame,) -> Union[str, List[Dict[str, Any]]]:
if self.result_format == utils.ChartDataResultFormat.CSV: if self.result_format == utils.ChartDataResultFormat.CSV:
@ -153,15 +155,15 @@ class QueryContext:
return df.to_dict(orient="records") return df.to_dict(orient="records")
def get_single_payload( def get_single_payload(
self, query_obj: QueryObject, **kwargs: Any self, query_obj: QueryObject, force_cached: Optional[bool] = False,
) -> Dict[str, Any]: ) -> Dict[str, Any]:
"""Returns a payload of metadata and data""" """Return results payload for a single quey"""
force_cached = kwargs.get("force_cached", False)
if self.result_type == utils.ChartDataResultType.QUERY: if self.result_type == utils.ChartDataResultType.QUERY:
return { return {
"query": self.datasource.get_query_str(query_obj.to_dict()), "query": self.datasource.get_query_str(query_obj.to_dict()),
"language": self.datasource.query_language, "language": self.datasource.query_language,
} }
if self.result_type == utils.ChartDataResultType.SAMPLES: if self.result_type == utils.ChartDataResultType.SAMPLES:
row_limit = query_obj.row_limit or math.inf row_limit = query_obj.row_limit or math.inf
query_obj = copy.copy(query_obj) query_obj = copy.copy(query_obj)
@ -173,10 +175,13 @@ class QueryContext:
query_obj.row_limit = min(row_limit, config["SAMPLES_ROW_LIMIT"]) query_obj.row_limit = min(row_limit, config["SAMPLES_ROW_LIMIT"])
query_obj.row_offset = 0 query_obj.row_offset = 0
query_obj.columns = [o.column_name for o in self.datasource.columns] query_obj.columns = [o.column_name for o in self.datasource.columns]
payload = self.get_df_payload(query_obj, force_cached=force_cached) payload = self.get_df_payload(query_obj, force_cached=force_cached)
df = payload["df"] df = payload["df"]
status = payload["status"] status = payload["status"]
if status != utils.QueryStatus.FAILED: if status != utils.QueryStatus.FAILED:
payload["colnames"] = list(df.columns)
payload["coltypes"] = utils.serialize_pandas_dtypes(df.dtypes)
payload["data"] = self.get_data(df) payload["data"] = self.get_data(df)
del payload["df"] del payload["df"]
@ -195,13 +200,19 @@ class QueryContext:
if col not in columns if col not in columns
] + rejected_time_columns ] + rejected_time_columns
if self.result_type == utils.ChartDataResultType.RESULTS: if (
self.result_type == utils.ChartDataResultType.RESULTS
and status != utils.QueryStatus.FAILED
):
return {"data": payload["data"]} return {"data": payload["data"]}
return payload return payload
def get_payload(self, **kwargs: Any) -> Dict[str, Any]: def get_payload(
cache_query_context = kwargs.get("cache_query_context", False) self,
force_cached = kwargs.get("force_cached", False) cache_query_context: Optional[bool] = False,
force_cached: Optional[bool] = False,
) -> Dict[str, Any]:
"""Returns the query results with both metadata and data"""
# Get all the payloads from the QueryObjects # Get all the payloads from the QueryObjects
query_results = [ query_results = [
@ -310,7 +321,7 @@ class QueryContext:
chart = ChartDAO.find_by_id(annotation_layer["value"]) chart = ChartDAO.find_by_id(annotation_layer["value"])
form_data = chart.form_data.copy() form_data = chart.form_data.copy()
if not chart: if not chart:
raise QueryObjectValidationError("The chart does not exist") raise QueryObjectValidationError(_("The chart does not exist"))
try: try:
viz_obj = get_viz( viz_obj = get_viz(
datasource_type=chart.datasource.type, datasource_type=chart.datasource.type,
@ -342,10 +353,9 @@ class QueryContext:
return annotation_data return annotation_data
def get_df_payload( # pylint: disable=too-many-statements,too-many-locals def get_df_payload( # pylint: disable=too-many-statements,too-many-locals
self, query_obj: QueryObject, **kwargs: Any self, query_obj: QueryObject, force_cached: Optional[bool] = False,
) -> Dict[str, Any]: ) -> Dict[str, Any]:
"""Handles caching around the df payload retrieval""" """Handles caching around the df payload retrieval"""
force_cached = kwargs.get("force_cached", False)
cache_key = self.query_cache_key(query_obj) cache_key = self.query_cache_key(query_obj)
logger.info("Cache key: %s", cache_key) logger.info("Cache key: %s", cache_key)
is_loaded = False is_loaded = False
@ -387,7 +397,7 @@ class QueryContext:
for col in query_obj.columns for col in query_obj.columns
+ query_obj.groupby + query_obj.groupby
+ utils.get_column_names_from_metrics(query_obj.metrics) + utils.get_column_names_from_metrics(query_obj.metrics)
if col not in self.datasource.column_names if col not in self.datasource.column_names and col != DTTM_ALIAS
] ]
if invalid_columns: if invalid_columns:
raise QueryObjectValidationError( raise QueryObjectValidationError(
@ -446,5 +456,6 @@ class QueryContext:
:raises SupersetSecurityException: If the user cannot access the resource :raises SupersetSecurityException: If the user cannot access the resource
""" """
for query in self.queries:
query.validate()
security_manager.raise_for_access(query_context=self) security_manager.raise_for_access(query_context=self)

View File

@ -28,7 +28,12 @@ from superset import app, is_feature_enabled
from superset.exceptions import QueryObjectValidationError from superset.exceptions import QueryObjectValidationError
from superset.typing import Metric from superset.typing import Metric
from superset.utils import pandas_postprocessing from superset.utils import pandas_postprocessing
from superset.utils.core import DTTM_ALIAS, get_metric_names, json_int_dttm_ser from superset.utils.core import (
DTTM_ALIAS,
find_duplicates,
get_metric_names,
json_int_dttm_ser,
)
from superset.utils.date_parser import get_since_until, parse_human_timedelta from superset.utils.date_parser import get_since_until, parse_human_timedelta
from superset.views.utils import get_time_range_endpoints from superset.views.utils import get_time_range_endpoints
@ -106,6 +111,8 @@ class QueryObject:
): ):
annotation_layers = annotation_layers or [] annotation_layers = annotation_layers or []
metrics = metrics or [] metrics = metrics or []
columns = columns or []
groupby = groupby or []
extras = extras or {} extras = extras or {}
is_sip_38 = is_feature_enabled("SIP_38_VIZ_REARCHITECTURE") is_sip_38 = is_feature_enabled("SIP_38_VIZ_REARCHITECTURE")
self.annotation_layers = [ self.annotation_layers = [
@ -126,19 +133,18 @@ class QueryObject:
time_range=time_range, time_range=time_range,
time_shift=time_shift, time_shift=time_shift,
) )
# is_timeseries is True if time column is in groupby # is_timeseries is True if time column is in either columns or groupby
# (both are dimensions)
self.is_timeseries = ( self.is_timeseries = (
is_timeseries is_timeseries
if is_timeseries is not None if is_timeseries is not None
else (DTTM_ALIAS in groupby if groupby else False) else DTTM_ALIAS in columns + groupby
) )
self.time_range = time_range self.time_range = time_range
self.time_shift = parse_human_timedelta(time_shift) self.time_shift = parse_human_timedelta(time_shift)
self.post_processing = [ self.post_processing = [
post_proc for post_proc in post_processing or [] if post_proc post_proc for post_proc in post_processing or [] if post_proc
] ]
if not is_sip_38:
self.groupby = groupby or []
# Support metric reference/definition in the format of # Support metric reference/definition in the format of
# 1. 'metric_name' - name of predefined metric # 1. 'metric_name' - name of predefined metric
@ -162,13 +168,16 @@ class QueryObject:
if config["SIP_15_ENABLED"] and "time_range_endpoints" not in self.extras: if config["SIP_15_ENABLED"] and "time_range_endpoints" not in self.extras:
self.extras["time_range_endpoints"] = get_time_range_endpoints(form_data={}) self.extras["time_range_endpoints"] = get_time_range_endpoints(form_data={})
self.columns = columns or [] self.columns = columns
if is_sip_38 and groupby: if is_sip_38:
self.columns += groupby if groupby:
logger.warning( logger.warning(
"The field `groupby` is deprecated. Viz plugins should " "The field `groupby` is deprecated. Viz plugins should "
"pass all selectables via the `columns` field" "pass all selectables via the `columns` field"
) )
self.columns += groupby
else:
self.groupby = groupby or []
self.orderby = orderby or [] self.orderby = orderby or []
@ -214,8 +223,34 @@ class QueryObject:
@property @property
def metric_names(self) -> List[str]: def metric_names(self) -> List[str]:
"""Return metrics names (labels), coerce adhoc metrics to strings."""
return get_metric_names(self.metrics) return get_metric_names(self.metrics)
@property
def column_names(self) -> List[str]:
"""Return column names (labels). Reserved for future adhoc calculated
columns."""
return self.columns
def validate(
self, raise_exceptions: Optional[bool] = True
) -> Optional[QueryObjectValidationError]:
"""Validate query object"""
error: Optional[QueryObjectValidationError] = None
all_labels = self.metric_names + self.column_names
if len(set(all_labels)) < len(all_labels):
dup_labels = find_duplicates(all_labels)
error = QueryObjectValidationError(
_(
"Duplicate column/metric labels: %(labels)s. Please make "
"sure all columns and metrics have a unique label.",
labels=", ".join(f'"{x}"' for x in dup_labels),
)
)
if error and raise_exceptions:
raise error
return error
def to_dict(self) -> Dict[str, Any]: def to_dict(self) -> Dict[str, Any]:
query_object_dict = { query_object_dict = {
"granularity": self.granularity, "granularity": self.granularity,

View File

@ -939,6 +939,7 @@ class SqlaTable( # pylint: disable=too-many-public-methods,too-many-instance-at
and (is_sip_38 or (not is_sip_38 and not groupby)) and (is_sip_38 or (not is_sip_38 and not groupby))
): ):
raise QueryObjectValidationError(_("Empty query?")) raise QueryObjectValidationError(_("Empty query?"))
metrics_exprs: List[ColumnElement] = [] metrics_exprs: List[ColumnElement] = []
for metric in metrics: for metric in metrics:
if utils.is_adhoc_metric(metric): if utils.is_adhoc_metric(metric):
@ -950,6 +951,7 @@ class SqlaTable( # pylint: disable=too-many-public-methods,too-many-instance-at
raise QueryObjectValidationError( raise QueryObjectValidationError(
_("Metric '%(metric)s' does not exist", metric=metric) _("Metric '%(metric)s' does not exist", metric=metric)
) )
if metrics_exprs: if metrics_exprs:
main_metric_expr = metrics_exprs[0] main_metric_expr = metrics_exprs[0]
else: else:
@ -960,14 +962,16 @@ class SqlaTable( # pylint: disable=too-many-public-methods,too-many-instance-at
groupby_exprs_sans_timestamp = OrderedDict() groupby_exprs_sans_timestamp = OrderedDict()
assert extras is not None assert extras is not None
if (is_sip_38 and metrics and columns) or (not is_sip_38 and groupby):
# dedup columns while preserving order
columns_ = columns if is_sip_38 else groupby
assert columns_
groupby = list(dict.fromkeys(columns_))
# filter out the pseudo column __timestamp from columns
columns = columns or []
columns = [col for col in columns if col != utils.DTTM_ALIAS]
if (is_sip_38 and metrics and columns) or (not is_sip_38 and metrics):
# dedup columns while preserving order
columns = columns if is_sip_38 else (groupby or columns)
select_exprs = [] select_exprs = []
for selected in groupby: for selected in columns:
# if groupby field/expr equals granularity field/expr # if groupby field/expr equals granularity field/expr
if selected == granularity: if selected == granularity:
time_grain = extras.get("time_grain_sqla") time_grain = extras.get("time_grain_sqla")
@ -979,7 +983,6 @@ class SqlaTable( # pylint: disable=too-many-public-methods,too-many-instance-at
else: else:
outer = literal_column(f"({selected})") outer = literal_column(f"({selected})")
outer = self.make_sqla_column_compatible(outer, selected) outer = self.make_sqla_column_compatible(outer, selected)
groupby_exprs_sans_timestamp[outer.name] = outer groupby_exprs_sans_timestamp[outer.name] = outer
select_exprs.append(outer) select_exprs.append(outer)
elif columns: elif columns:
@ -1001,7 +1004,8 @@ class SqlaTable( # pylint: disable=too-many-public-methods,too-many-instance-at
if is_timeseries: if is_timeseries:
timestamp = dttm_col.get_timestamp_expression(time_grain) timestamp = dttm_col.get_timestamp_expression(time_grain)
select_exprs += [timestamp] # always put timestamp as the first column
select_exprs.insert(0, timestamp)
groupby_exprs_with_timestamp[timestamp.name] = timestamp groupby_exprs_with_timestamp[timestamp.name] = timestamp
# Use main dttm column to support index with secondary dttm columns. # Use main dttm column to support index with secondary dttm columns.

View File

@ -158,28 +158,22 @@ class BaseEngineSpec: # pylint: disable=too-many-public-methods
try_remove_schema_from_table_name = True # pylint: disable=invalid-name try_remove_schema_from_table_name = True # pylint: disable=invalid-name
run_multiple_statements_as_one = False run_multiple_statements_as_one = False
# default matching patterns for identifying column types # default matching patterns to convert database specific column types to
db_column_types: Dict[utils.GenericDataType, Tuple[Pattern[Any], ...]] = { # more generic types
db_column_types: Dict[utils.GenericDataType, Tuple[Pattern[str], ...]] = {
utils.GenericDataType.NUMERIC: ( utils.GenericDataType.NUMERIC: (
re.compile(r"BIT", re.IGNORECASE), re.compile(r"BIT", re.IGNORECASE),
re.compile(r".*DOUBLE.*", re.IGNORECASE), re.compile(
re.compile(r".*FLOAT.*", re.IGNORECASE), r".*(DOUBLE|FLOAT|INT|NUMBER|REAL|NUMERIC|DECIMAL|MONEY).*",
re.compile(r".*INT.*", re.IGNORECASE), re.IGNORECASE,
re.compile(r".*NUMBER.*", re.IGNORECASE), ),
re.compile(r".*LONG$", re.IGNORECASE), re.compile(r".*LONG$", re.IGNORECASE),
re.compile(r".*REAL.*", re.IGNORECASE),
re.compile(r".*NUMERIC.*", re.IGNORECASE),
re.compile(r".*DECIMAL.*", re.IGNORECASE),
re.compile(r".*MONEY.*", re.IGNORECASE),
), ),
utils.GenericDataType.STRING: ( utils.GenericDataType.STRING: (
re.compile(r".*CHAR.*", re.IGNORECASE), re.compile(r".*(CHAR|STRING|TEXT).*", re.IGNORECASE),
re.compile(r".*STRING.*", re.IGNORECASE),
re.compile(r".*TEXT.*", re.IGNORECASE),
), ),
utils.GenericDataType.TEMPORAL: ( utils.GenericDataType.TEMPORAL: (
re.compile(r".*DATE.*", re.IGNORECASE), re.compile(r".*(DATE|TIME).*", re.IGNORECASE),
re.compile(r".*TIME.*", re.IGNORECASE),
), ),
} }

View File

@ -373,7 +373,11 @@ class Database(
username = utils.get_username() username = utils.get_username()
def needs_conversion(df_series: pd.Series) -> bool: def needs_conversion(df_series: pd.Series) -> bool:
return not df_series.empty and isinstance(df_series[0], (list, dict)) return (
not df_series.empty
and isinstance(df_series, pd.Series)
and isinstance(df_series[0], (list, dict))
)
def _log_query(sql: str) -> None: def _log_query(sql: str) -> None:
if log_query: if log_query:
@ -397,9 +401,9 @@ class Database(
if mutator: if mutator:
mutator(df) mutator(df)
for k, v in df.dtypes.items(): for col, coltype in df.dtypes.to_dict().items():
if v.type == numpy.object_ and needs_conversion(df[k]): if coltype == numpy.object_ and needs_conversion(df[col]):
df[k] = df[k].apply(utils.json_dumps_w_dates) df[col] = df[col].apply(utils.json_dumps_w_dates)
return df return df

View File

@ -15,6 +15,7 @@
# specific language governing permissions and limitations # specific language governing permissions and limitations
# under the License. # under the License.
"""Utility functions used across Superset""" """Utility functions used across Superset"""
import collections
import decimal import decimal
import errno import errno
import functools import functools
@ -37,7 +38,7 @@ from email.mime.image import MIMEImage
from email.mime.multipart import MIMEMultipart from email.mime.multipart import MIMEMultipart
from email.mime.text import MIMEText from email.mime.text import MIMEText
from email.utils import formatdate from email.utils import formatdate
from enum import Enum from enum import Enum, IntEnum
from timeit import default_timer from timeit import default_timer
from types import TracebackType from types import TracebackType
from typing import ( from typing import (
@ -55,6 +56,7 @@ from typing import (
Tuple, Tuple,
Type, Type,
TYPE_CHECKING, TYPE_CHECKING,
TypeVar,
Union, Union,
) )
from urllib.parse import unquote_plus from urllib.parse import unquote_plus
@ -105,6 +107,8 @@ DTTM_ALIAS = "__timestamp"
JS_MAX_INTEGER = 9007199254740991 # Largest int Java Script can handle 2^53-1 JS_MAX_INTEGER = 9007199254740991 # Largest int Java Script can handle 2^53-1
InputType = TypeVar("InputType")
class LenientEnum(Enum): class LenientEnum(Enum):
"""Enums with a `get` method that convert a enum value to `Enum` if it is a """Enums with a `get` method that convert a enum value to `Enum` if it is a
@ -130,14 +134,15 @@ class AnnotationType(str, Enum):
TIME_SERIES = "TIME_SERIES" TIME_SERIES = "TIME_SERIES"
class GenericDataType(Enum): class GenericDataType(IntEnum):
""" """
Generic database column type Generic database column type that fits both frontend and backend.
""" """
NUMERIC = 0 NUMERIC = 0
STRING = 1 STRING = 1
TEMPORAL = 2 TEMPORAL = 2
BOOLEAN = 3
class ChartDataResultFormat(str, Enum): class ChartDataResultFormat(str, Enum):
@ -1396,6 +1401,21 @@ def get_column_names_from_metrics(metrics: List[Metric]) -> List[str]:
return columns return columns
def serialize_pandas_dtypes(dtypes: List[np.dtype]) -> List[GenericDataType]:
"""Serialize pandas/numpy dtypes to JavaScript types"""
mapping = {
"object": GenericDataType.STRING,
"category": GenericDataType.STRING,
"datetime64[ns]": GenericDataType.TEMPORAL,
"int64": GenericDataType.NUMERIC,
"in32": GenericDataType.NUMERIC,
"float64": GenericDataType.NUMERIC,
"float32": GenericDataType.NUMERIC,
"bool": GenericDataType.BOOLEAN,
}
return [mapping.get(str(x), GenericDataType.STRING) for x in dtypes]
def indexed( def indexed(
items: List[Any], key: Union[str, Callable[[Any], Any]] items: List[Any], key: Union[str, Callable[[Any], Any]]
) -> Dict[Any, List[Any]]: ) -> Dict[Any, List[Any]]:
@ -1481,3 +1501,8 @@ def get_time_filter_status( # pylint: disable=too-many-branches
def format_list(items: Sequence[str], sep: str = ", ", quote: str = '"') -> str: def format_list(items: Sequence[str], sep: str = ", ", quote: str = '"') -> str:
quote_escaped = "\\" + quote quote_escaped = "\\" + quote
return sep.join(f"{quote}{x.replace(quote, quote_escaped)}{quote}" for x in items) return sep.join(f"{quote}{x.replace(quote, quote_escaped)}{quote}" for x in items)
def find_duplicates(items: Iterable[InputType]) -> List[InputType]:
"""Find duplicate items in an iterable."""
return [item for item, count in collections.Counter(items).items() if count > 1]

View File

@ -16,7 +16,7 @@
# under the License. # under the License.
import logging import logging
from functools import partial from functools import partial
from typing import Any, Callable, cast, Dict, List, Optional, Set, Tuple, Union from typing import Any, Callable, Dict, List, Optional, Set, Tuple, Union
import geohash as geohash_lib import geohash as geohash_lib
import numpy as np import numpy as np
@ -554,29 +554,53 @@ def geodetic_parse(
raise QueryObjectValidationError(_("Invalid geodetic string")) raise QueryObjectValidationError(_("Invalid geodetic string"))
@validate_column_args("columns")
def contribution( def contribution(
df: DataFrame, orientation: PostProcessingContributionOrientation df: DataFrame,
orientation: Optional[
PostProcessingContributionOrientation
] = PostProcessingContributionOrientation.COLUMN,
columns: Optional[List[str]] = None,
rename_columns: Optional[List[str]] = None,
) -> DataFrame: ) -> DataFrame:
""" """
Calculate cell contibution to row/column total. Calculate cell contibution to row/column total for numeric columns.
Non-numeric columns will be kept untouched.
If `columns` are specified, only calculate contributions on selected columns.
:param df: DataFrame containing all-numeric data (temporal column ignored) :param df: DataFrame containing all-numeric data (temporal column ignored)
:param columns: Columns to calculate values from.
:param rename_columns: The new labels for the calculated contribution columns.
The original columns will not be removed.
:param orientation: calculate by dividing cell with row/column total :param orientation: calculate by dividing cell with row/column total
:return: DataFrame with contributions, with temporal column at beginning if present :return: DataFrame with contributions.
""" """
temporal_series: Optional[Series] = None
contribution_df = df.copy() contribution_df = df.copy()
if DTTM_ALIAS in df.columns: numeric_df = contribution_df.select_dtypes(include="number")
temporal_series = cast(Series, contribution_df.pop(DTTM_ALIAS)) # verify column selections
if columns:
if orientation == PostProcessingContributionOrientation.ROW: numeric_columns = numeric_df.columns.tolist()
contribution_dft = contribution_df.T for col in columns:
contribution_df = (contribution_dft / contribution_dft.sum()).T if col not in numeric_columns:
else: raise QueryObjectValidationError(
contribution_df = contribution_df / contribution_df.sum() _(
'Column "%(column)s" is not numeric or does not '
if temporal_series is not None: "exists in the query results.",
contribution_df.insert(0, DTTM_ALIAS, temporal_series) column=col,
)
)
columns = columns or numeric_df.columns
rename_columns = rename_columns or columns
if len(rename_columns) != len(columns):
raise QueryObjectValidationError(
_("`rename_columns` must have the same length as `columns`.")
)
# limit to selected columns
numeric_df = numeric_df[columns]
axis = 0 if orientation == PostProcessingContributionOrientation.COLUMN else 1
numeric_df = numeric_df / numeric_df.values.sum(axis=axis, keepdims=True)
contribution_df[rename_columns] = numeric_df
return contribution_df return contribution_df

View File

@ -21,13 +21,17 @@ from superset.utils.core import AnnotationType, DTTM_ALIAS
from tests.base_tests import get_table_by_name from tests.base_tests import get_table_by_name
query_birth_names = { query_birth_names = {
"extras": {"where": "", "time_range_endpoints": ["inclusive", "exclusive"]}, "extras": {
"granularity": "ds", "where": "",
"time_range_endpoints": ["inclusive", "exclusive"],
"time_grain_sqla": "P1D",
},
"groupby": ["name"], "groupby": ["name"],
"metrics": [{"label": "sum__num"}], "metrics": [{"label": "sum__num"}],
"order_desc": True, "order_desc": True,
"orderby": [["sum__num", False]], "orderby": [["sum__num", False]],
"row_limit": 100, "row_limit": 100,
"granularity": "ds",
"time_range": "100 years ago : now", "time_range": "100 years ago : now",
"timeseries_limit": 0, "timeseries_limit": 0,
"timeseries_limit_metric": None, "timeseries_limit_metric": None,

View File

@ -524,19 +524,40 @@ class TestPostProcessing(SupersetTestCase):
"b": [1, 9], "b": [1, 9],
} }
) )
with pytest.raises(QueryObjectValidationError, match="not numeric"):
proc.contribution(df, columns=[DTTM_ALIAS])
with pytest.raises(QueryObjectValidationError, match="same length"):
proc.contribution(df, columns=["a"], rename_columns=["aa", "bb"])
# cell contribution across row # cell contribution across row
row_df = proc.contribution(df, PostProcessingContributionOrientation.ROW) processed_df = proc.contribution(
self.assertListEqual(df.columns.tolist(), [DTTM_ALIAS, "a", "b"]) df, orientation=PostProcessingContributionOrientation.ROW,
self.assertListEqual(series_to_list(row_df["a"]), [0.5, 0.25]) )
self.assertListEqual(series_to_list(row_df["b"]), [0.5, 0.75]) self.assertListEqual(processed_df.columns.tolist(), [DTTM_ALIAS, "a", "b"])
self.assertListEqual(processed_df["a"].tolist(), [0.5, 0.25])
self.assertListEqual(processed_df["b"].tolist(), [0.5, 0.75])
# cell contribution across column without temporal column # cell contribution across column without temporal column
df.pop(DTTM_ALIAS) df.pop(DTTM_ALIAS)
column_df = proc.contribution(df, PostProcessingContributionOrientation.COLUMN) processed_df = proc.contribution(
self.assertListEqual(df.columns.tolist(), ["a", "b"]) df, orientation=PostProcessingContributionOrientation.COLUMN
self.assertListEqual(series_to_list(column_df["a"]), [0.25, 0.75]) )
self.assertListEqual(series_to_list(column_df["b"]), [0.1, 0.9]) self.assertListEqual(processed_df.columns.tolist(), ["a", "b"])
self.assertListEqual(processed_df["a"].tolist(), [0.25, 0.75])
self.assertListEqual(processed_df["b"].tolist(), [0.1, 0.9])
# contribution only on selected columns
processed_df = proc.contribution(
df,
orientation=PostProcessingContributionOrientation.COLUMN,
columns=["a"],
rename_columns=["pct_a"],
)
self.assertListEqual(processed_df.columns.tolist(), ["a", "b", "pct_a"])
self.assertListEqual(processed_df["a"].tolist(), [1, 3])
self.assertListEqual(processed_df["b"].tolist(), [1, 9])
self.assertListEqual(processed_df["pct_a"].tolist(), [0.25, 0.75])
def test_prophet_valid(self): def test_prophet_valid(self):
pytest.importorskip("fbprophet") pytest.importorskip("fbprophet")

View File

@ -25,7 +25,6 @@ from superset.utils.core import (
AdhocMetricExpressionType, AdhocMetricExpressionType,
ChartDataResultFormat, ChartDataResultFormat,
ChartDataResultType, ChartDataResultType,
FilterOperator,
TimeRangeEndpoint, TimeRangeEndpoint,
) )
from tests.base_tests import SupersetTestCase from tests.base_tests import SupersetTestCase

View File

@ -20,7 +20,6 @@ from typing import Any, Dict, NamedTuple, List, Pattern, Tuple, Union
from unittest.mock import patch from unittest.mock import patch
import pytest import pytest
import tests.test_app
from superset import db from superset import db
from superset.connectors.sqla.models import SqlaTable, TableColumn from superset.connectors.sqla.models import SqlaTable, TableColumn
from superset.db_engine_specs.druid import DruidEngineSpec from superset.db_engine_specs.druid import DruidEngineSpec