diff --git a/superset-frontend/src/SqlLab/actions/sqlLab.js b/superset-frontend/src/SqlLab/actions/sqlLab.js index fd23cb57538..a4aead7e129 100644 --- a/superset-frontend/src/SqlLab/actions/sqlLab.js +++ b/superset-frontend/src/SqlLab/actions/sqlLab.js @@ -1098,12 +1098,14 @@ export function reFetchQueryResults(query) { export function expandTable(table) { return function (dispatch) { - const sync = isFeatureEnabled(FeatureFlag.SqllabBackendPersistence) - ? SupersetClient.post({ - endpoint: encodeURI(`/tableschemaview/${table.id}/expanded`), - postPayload: { expanded: true }, - }) - : Promise.resolve(); + const sync = + isFeatureEnabled(FeatureFlag.SqllabBackendPersistence) && + table.initialized + ? SupersetClient.post({ + endpoint: encodeURI(`/tableschemaview/${table.id}/expanded`), + postPayload: { expanded: true }, + }) + : Promise.resolve(); return sync .then(() => dispatch({ type: EXPAND_TABLE, table })) @@ -1122,12 +1124,14 @@ export function expandTable(table) { export function collapseTable(table) { return function (dispatch) { - const sync = isFeatureEnabled(FeatureFlag.SqllabBackendPersistence) - ? SupersetClient.post({ - endpoint: encodeURI(`/tableschemaview/${table.id}/expanded`), - postPayload: { expanded: false }, - }) - : Promise.resolve(); + const sync = + isFeatureEnabled(FeatureFlag.SqllabBackendPersistence) && + table.initialized + ? SupersetClient.post({ + endpoint: encodeURI(`/tableschemaview/${table.id}/expanded`), + postPayload: { expanded: false }, + }) + : Promise.resolve(); return sync .then(() => dispatch({ type: COLLAPSE_TABLE, table })) diff --git a/superset-frontend/src/SqlLab/actions/sqlLab.test.js b/superset-frontend/src/SqlLab/actions/sqlLab.test.js index a90355b8762..ac6263d72dd 100644 --- a/superset-frontend/src/SqlLab/actions/sqlLab.test.js +++ b/superset-frontend/src/SqlLab/actions/sqlLab.test.js @@ -934,6 +934,10 @@ describe('async actions', () => { fetchMock.delete(updateTableSchemaEndpoint, {}); fetchMock.post(updateTableSchemaEndpoint, JSON.stringify({ id: 1 })); + const updateTableSchemaExpandedEndpoint = + 'glob:**/tableschemaview/*/expanded'; + fetchMock.post(updateTableSchemaExpandedEndpoint, {}); + const getTableMetadataEndpoint = 'glob:**/api/v1/database/*/table_metadata/*'; fetchMock.get(getTableMetadataEndpoint, {}); @@ -1411,10 +1415,10 @@ describe('async actions', () => { // eslint-disable-next-line no-restricted-globals -- TODO: Migrate from describe blocks describe('expandTable', () => { - test('updates the table schema state in the backend', () => { + test('updates the table schema state in the backend when initialized', () => { expect.assertions(2); - const table = { id: 1 }; + const table = { id: 1, initialized: true }; const store = mockStore({}); const expectedActions = [ { @@ -1424,17 +1428,108 @@ describe('async actions', () => { ]; return store.dispatch(actions.expandTable(table)).then(() => { expect(store.getActions()).toEqual(expectedActions); - expect(fetchMock.calls(updateTableSchemaEndpoint)).toHaveLength(1); + const expandedCalls = fetchMock + .calls() + .filter( + call => + call[0] && + call[0].includes('/tableschemaview/') && + call[0].includes('/expanded'), + ); + expect(expandedCalls).toHaveLength(1); + }); + }); + + test('does not call backend when table is not initialized', () => { + expect.assertions(2); + + const table = { id: 'yVJPtuSackF', initialized: false }; + const store = mockStore({}); + const expectedActions = [ + { + type: actions.EXPAND_TABLE, + table, + }, + ]; + return store.dispatch(actions.expandTable(table)).then(() => { + expect(store.getActions()).toEqual(expectedActions); + // Check all POST calls to find the expanded endpoint + const expandedCalls = fetchMock + .calls() + .filter( + call => + call[0] && + call[0].includes('/tableschemaview/') && + call[0].includes('/expanded'), + ); + expect(expandedCalls).toHaveLength(0); + }); + }); + + test('does not call backend when initialized is undefined', () => { + expect.assertions(2); + + const table = { id: 'yVJPtuSackF' }; + const store = mockStore({}); + const expectedActions = [ + { + type: actions.EXPAND_TABLE, + table, + }, + ]; + return store.dispatch(actions.expandTable(table)).then(() => { + expect(store.getActions()).toEqual(expectedActions); + // Check all POST calls to find the expanded endpoint + const expandedCalls = fetchMock + .calls() + .filter( + call => + call[0] && + call[0].includes('/tableschemaview/') && + call[0].includes('/expanded'), + ); + expect(expandedCalls).toHaveLength(0); + }); + }); + + test('does not call backend when feature flag is off', () => { + expect.assertions(2); + + isFeatureEnabled.mockImplementation( + feature => !(feature === 'SQLLAB_BACKEND_PERSISTENCE'), + ); + + const table = { id: 1, initialized: true }; + const store = mockStore({}); + const expectedActions = [ + { + type: actions.EXPAND_TABLE, + table, + }, + ]; + return store.dispatch(actions.expandTable(table)).then(() => { + expect(store.getActions()).toEqual(expectedActions); + // Check all POST calls to find the expanded endpoint + const expandedCalls = fetchMock + .calls() + .filter( + call => + call[0] && + call[0].includes('/tableschemaview/') && + call[0].includes('/expanded'), + ); + expect(expandedCalls).toHaveLength(0); + isFeatureEnabled.mockRestore(); }); }); }); // eslint-disable-next-line no-restricted-globals -- TODO: Migrate from describe blocks describe('collapseTable', () => { - test('updates the table schema state in the backend', () => { + test('updates the table schema state in the backend when initialized', () => { expect.assertions(2); - const table = { id: 1 }; + const table = { id: 1, initialized: true }; const store = mockStore({}); const expectedActions = [ { @@ -1444,7 +1539,95 @@ describe('async actions', () => { ]; return store.dispatch(actions.collapseTable(table)).then(() => { expect(store.getActions()).toEqual(expectedActions); - expect(fetchMock.calls(updateTableSchemaEndpoint)).toHaveLength(1); + const expandedCalls = fetchMock + .calls() + .filter( + call => + call[0] && + call[0].includes('/tableschemaview/') && + call[0].includes('/expanded'), + ); + expect(expandedCalls).toHaveLength(1); + }); + }); + + test('does not call backend when table is not initialized', () => { + expect.assertions(2); + + const table = { id: 'yVJPtuSackF', initialized: false }; + const store = mockStore({}); + const expectedActions = [ + { + type: actions.COLLAPSE_TABLE, + table, + }, + ]; + return store.dispatch(actions.collapseTable(table)).then(() => { + expect(store.getActions()).toEqual(expectedActions); + const expandedCalls = fetchMock + .calls() + .filter( + call => + call[0] && + call[0].includes('/tableschemaview/') && + call[0].includes('/expanded'), + ); + expect(expandedCalls).toHaveLength(0); + }); + }); + + test('does not call backend when initialized is undefined', () => { + expect.assertions(2); + + const table = { id: 'yVJPtuSackF' }; + const store = mockStore({}); + const expectedActions = [ + { + type: actions.COLLAPSE_TABLE, + table, + }, + ]; + return store.dispatch(actions.collapseTable(table)).then(() => { + expect(store.getActions()).toEqual(expectedActions); + const expandedCalls = fetchMock + .calls() + .filter( + call => + call[0] && + call[0].includes('/tableschemaview/') && + call[0].includes('/expanded'), + ); + expect(expandedCalls).toHaveLength(0); + }); + }); + + test('does not call backend when feature flag is off', () => { + expect.assertions(2); + + isFeatureEnabled.mockImplementation( + feature => !(feature === 'SQLLAB_BACKEND_PERSISTENCE'), + ); + + const table = { id: 1, initialized: true }; + const store = mockStore({}); + const expectedActions = [ + { + type: actions.COLLAPSE_TABLE, + table, + }, + ]; + return store.dispatch(actions.collapseTable(table)).then(() => { + expect(store.getActions()).toEqual(expectedActions); + const expandedCalls = fetchMock + .calls() + .filter( + call => + call[0] && + call[0].includes('/tableschemaview/') && + call[0].includes('/expanded'), + ); + expect(expandedCalls).toHaveLength(0); + isFeatureEnabled.mockRestore(); }); }); });