feat(sqllab): Add a configuration option to disable data preview (#19104)

This commit is contained in:
cccs-Dustin
2022-03-17 09:22:57 -04:00
committed by GitHub
parent cfb967f430
commit 02ef9ca4cd
18 changed files with 175 additions and 42 deletions

View File

@@ -1018,28 +1018,13 @@ function getTableMetadata(table, query, dispatch) {
),
})
.then(({ json }) => {
const dataPreviewQuery = {
id: shortid.generate(),
dbId: query.dbId,
sql: json.selectStar,
tableName: table.name,
sqlEditorId: null,
tab: '',
runAsync: false,
ctas: false,
isDataPreview: true,
};
const newTable = {
...table,
...json,
expanded: true,
isMetadataLoading: false,
dataPreviewQueryId: dataPreviewQuery.id,
};
Promise.all([
dispatch(mergeTable(newTable, dataPreviewQuery)), // Merge table to tables in state
dispatch(runQuery(dataPreviewQuery)), // Run query to get preview data for table
]);
dispatch(mergeTable(newTable)); // Merge table to tables in state
return newTable;
})
.catch(() =>
@@ -1082,7 +1067,7 @@ function getTableExtendedMetadata(table, query, dispatch) {
);
}
export function addTable(query, tableName, schemaName) {
export function addTable(query, database, tableName, schemaName) {
return function (dispatch) {
const table = {
dbId: query.dbId,
@@ -1110,6 +1095,32 @@ export function addTable(query, tableName, schemaName) {
})
: Promise.resolve({ json: { id: shortid.generate() } });
if (!database.disable_data_preview && database.id === query.dbId) {
const dataPreviewQuery = {
id: shortid.generate(),
dbId: query.dbId,
sql: newTable.selectStar,
tableName: table.name,
sqlEditorId: null,
tab: '',
runAsync: database.allow_run_async,
ctas: false,
isDataPreview: true,
};
Promise.all([
dispatch(
mergeTable(
{
...newTable,
dataPreviewQueryId: dataPreviewQuery.id,
},
dataPreviewQuery,
),
),
dispatch(runQuery(dataPreviewQuery)),
]);
}
return sync
.then(({ json: resultJson }) =>
dispatch(mergeTable({ ...table, id: resultJson.id })),

View File

@@ -727,28 +727,18 @@ describe('async actions', () => {
it('updates the table schema state in the backend', () => {
expect.assertions(5);
const results = {
data: mockBigNumber,
query: { sqlEditorId: 'null' },
query_id: 'efgh',
};
fetchMock.post(runQueryEndpoint, JSON.stringify(results), {
overwriteRoutes: true,
});
const database = { disable_data_preview: true };
const tableName = 'table';
const schemaName = 'schema';
const store = mockStore({});
const expectedActionTypes = [
actions.MERGE_TABLE, // addTable
actions.MERGE_TABLE, // getTableMetadata
actions.START_QUERY, // runQuery (data preview)
actions.MERGE_TABLE, // getTableExtendedMetadata
actions.QUERY_SUCCESS, // querySuccess
actions.MERGE_TABLE, // addTable
];
return store
.dispatch(actions.addTable(query, tableName, schemaName))
.dispatch(actions.addTable(query, database, tableName, schemaName))
.then(() => {
expect(store.getActions().map(a => a.type)).toEqual(
expectedActionTypes,
@@ -759,6 +749,47 @@ describe('async actions', () => {
1,
);
// tab state is not updated, since no query was run
expect(fetchMock.calls(updateTabStateEndpoint)).toHaveLength(0);
});
});
it('updates and runs data preview query when configured', () => {
expect.assertions(5);
const results = {
data: mockBigNumber,
query: { sqlEditorId: 'null', dbId: 1 },
query_id: 'efgh',
};
fetchMock.post(runQueryEndpoint, JSON.stringify(results), {
overwriteRoutes: true,
});
const database = { disable_data_preview: false, id: 1 };
const tableName = 'table';
const schemaName = 'schema';
const store = mockStore({});
const expectedActionTypes = [
actions.MERGE_TABLE, // addTable
actions.MERGE_TABLE, // getTableMetadata
actions.MERGE_TABLE, // getTableExtendedMetadata
actions.MERGE_TABLE, // addTable (data preview)
actions.START_QUERY, // runQuery (data preview)
actions.MERGE_TABLE, // addTable
actions.QUERY_SUCCESS, // querySuccess
];
return store
.dispatch(actions.addTable(query, database, tableName, schemaName))
.then(() => {
expect(store.getActions().map(a => a.type)).toEqual(
expectedActionTypes,
);
expect(fetchMock.calls(updateTableSchemaEndpoint)).toHaveLength(1);
expect(fetchMock.calls(getTableMetadataEndpoint)).toHaveLength(1);
expect(fetchMock.calls(getExtraTableMetadataEndpoint)).toHaveLength(
1,
);
// tab state is not updated, since the query is a data preview
expect(fetchMock.calls(updateTabStateEndpoint)).toHaveLength(0);
});

View File

@@ -43,11 +43,17 @@ interface Props {
actions: {
queryEditorSetSelectedText: (edit: any, text: null | string) => void;
queryEditorSetFunctionNames: (queryEditor: object, dbId: number) => void;
addTable: (queryEditor: any, value: any, schema: any) => void;
addTable: (
queryEditor: any,
database: any,
value: any,
schema: any,
) => void;
};
autocomplete: boolean;
onBlur: (sql: string) => void;
sql: string;
database: any;
schemas: any[];
tables: any[];
functionNames: string[];
@@ -210,6 +216,7 @@ class AceEditorWrapper extends React.PureComponent<Props, State> {
if (data.meta === 'table') {
this.props.actions.addTable(
this.props.queryEditor,
this.props.database,
data.value,
this.props.queryEditor.schema,
);

View File

@@ -514,6 +514,7 @@ class SqlEditor extends React.PureComponent {
onChange={this.onSqlChanged}
queryEditor={this.props.queryEditor}
sql={this.props.queryEditor.sql}
database={this.props.database}
schemas={this.props.queryEditor.schemaOptions}
tables={this.props.queryEditor.tableOptions}
functionNames={this.props.queryEditor.functionNames}

View File

@@ -36,7 +36,7 @@ interface actionsTypes {
queryEditorSetFunctionNames: (queryEditor: QueryEditor, dbId: number) => void;
collapseTable: (table: Table) => void;
expandTable: (table: Table) => void;
addTable: (queryEditor: any, value: any, schema: any) => void;
addTable: (queryEditor: any, database: any, value: any, schema: any) => void;
setDatabases: (arg0: any) => {};
addDangerToast: (msg: string) => void;
queryEditorSetSchema: (queryEditor: QueryEditor, schema?: string) => void;
@@ -103,7 +103,7 @@ export default function SqlEditorLeftBar({
const onTableChange = (tableName: string, schemaName: string) => {
if (tableName && schemaName) {
actions.addTable(queryEditor, tableName, schemaName);
actions.addTable(queryEditor, database, tableName, schemaName);
}
};

View File

@@ -76,6 +76,7 @@ beforeEach(() => {
allows_cost_estimate: 'Allows Cost Estimate',
allows_subquery: 'Allows Subquery',
allows_virtual_table_explore: 'Allows Virtual Table Explore',
disable_data_preview: 'Disables SQL Lab Data Preview',
backend: 'Backend',
changed_on: 'Changed On',
changed_on_delta_humanized: 'Changed On Delta Humanized',
@@ -97,6 +98,7 @@ beforeEach(() => {
'allows_cost_estimate',
'allows_subquery',
'allows_virtual_table_explore',
'disable_data_preview',
'backend',
'changed_on',
'changed_on_delta_humanized',
@@ -130,6 +132,7 @@ beforeEach(() => {
allows_cost_estimate: null,
allows_subquery: true,
allows_virtual_table_explore: true,
disable_data_preview: false,
backend: 'postgresql',
changed_on: '2021-03-09T19:02:07.141095',
changed_on_delta_humanized: 'a day ago',
@@ -150,6 +153,7 @@ beforeEach(() => {
allows_cost_estimate: null,
allows_subquery: true,
allows_virtual_table_explore: true,
disable_data_preview: false,
backend: 'mysql',
changed_on: '2021-03-09T19:02:07.141095',
changed_on_delta_humanized: 'a day ago',

View File

@@ -182,7 +182,7 @@ const ExtraOptions = ({
/>
</div>
</StyledInputContainer>
<StyledInputContainer>
<StyledInputContainer css={no_margin_bottom}>
<div className="input-container">
<IndeterminateCheckbox
id="allows_virtual_table_explore"
@@ -198,6 +198,24 @@ const ExtraOptions = ({
/>
</div>
</StyledInputContainer>
<StyledInputContainer>
<div className="input-container">
<IndeterminateCheckbox
id="disable_data_preview"
indeterminate={false}
checked={!!db?.extra_json?.disable_data_preview}
onChange={onExtraInputChange}
labelText={t('Disable SQL Lab data preview queries')}
/>
<InfoTooltip
tooltip={t(
'Disable data preview when fetching table metadata in SQL Lab. ' +
' Useful to avoid browser performance issues when using ' +
' databases with very wide tables.',
)}
/>
</div>
</StyledInputContainer>
</StyledExpandableForm>
</StyledInputContainer>
</Collapse.Panel>

View File

@@ -591,6 +591,15 @@ describe('DatabaseModal', () => {
const allowDbExplorationText = screen.getByText(
/allow this database to be explored/i,
);
const disableSQLLabDataPreviewQueriesCheckbox = screen.getByRole(
'checkbox',
{
name: /Disable SQL Lab data preview queries/i,
},
);
const disableSQLLabDataPreviewQueriesText = screen.getByText(
/Disable SQL Lab data preview queries/i,
);
// ---------- Assertions ----------
const visibleComponents = [
@@ -610,6 +619,7 @@ describe('DatabaseModal', () => {
checkboxOffSVGs[4],
checkboxOffSVGs[5],
checkboxOffSVGs[6],
checkboxOffSVGs[7],
tooltipIcons[0],
tooltipIcons[1],
tooltipIcons[2],
@@ -617,6 +627,7 @@ describe('DatabaseModal', () => {
tooltipIcons[4],
tooltipIcons[5],
tooltipIcons[6],
tooltipIcons[7],
exposeInSQLLabText,
allowCTASText,
allowCVASText,
@@ -627,6 +638,7 @@ describe('DatabaseModal', () => {
allowMultiSchemaMDFetchText,
enableQueryCostEstimationText,
allowDbExplorationText,
disableSQLLabDataPreviewQueriesText,
];
// These components exist in the DOM but are not visible
const invisibleComponents = [
@@ -637,6 +649,7 @@ describe('DatabaseModal', () => {
allowMultiSchemaMDFetchCheckbox,
enableQueryCostEstimationCheckbox,
allowDbExplorationCheckbox,
disableSQLLabDataPreviewQueriesCheckbox,
];
visibleComponents.forEach(component => {
@@ -645,8 +658,8 @@ describe('DatabaseModal', () => {
invisibleComponents.forEach(component => {
expect(component).not.toBeVisible();
});
expect(checkboxOffSVGs).toHaveLength(7);
expect(tooltipIcons).toHaveLength(7);
expect(checkboxOffSVGs).toHaveLength(8);
expect(tooltipIcons).toHaveLength(8);
});
it('renders the "Advanced" - PERFORMANCE tab correctly', async () => {

View File

@@ -92,6 +92,7 @@ export type DatabaseObject = {
version?: string;
cost_estimate_enabled?: boolean; // in SQL Lab
disable_data_preview?: boolean; // in SQL Lab
};
// Temporary storage