This is an automated email from the ASF dual-hosted git repository.

johnbodley pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/superset.git


The following commit(s) were added to refs/heads/master by this push:
     new 4980621902 feat(sqllab): Add event logger (#23040)
4980621902 is described below

commit 4980621902716205c2d531a36850d201ebb3a79c
Author: JUST.in DO IT <[email protected]>
AuthorDate: Mon Feb 13 09:47:28 2023 -0800

    feat(sqllab): Add event logger (#23040)
---
 superset-frontend/src/SqlLab/App.jsx               |  3 +-
 .../src/SqlLab/components/App/App.test.jsx         | 49 ++++++++++++++++++----
 .../src/SqlLab/components/App/index.jsx            | 17 ++++++--
 superset-frontend/src/logger/LogUtils.ts           |  2 +
 .../src/middleware/loggerMiddleware.js             | 12 +++++-
 5 files changed, 69 insertions(+), 14 deletions(-)

diff --git a/superset-frontend/src/SqlLab/App.jsx 
b/superset-frontend/src/SqlLab/App.jsx
index a3a939aabd..b75ea5c624 100644
--- a/superset-frontend/src/SqlLab/App.jsx
+++ b/superset-frontend/src/SqlLab/App.jsx
@@ -31,6 +31,7 @@ import {
 } from 'src/featureFlags';
 import setupExtensions from 'src/setup/setupExtensions';
 import getBootstrapData from 'src/utils/getBootstrapData';
+import logger from 'src/middleware/loggerMiddleware';
 import getInitialState from './reducers/getInitialState';
 import rootReducer from './reducers/index';
 import { initEnhancer } from '../reduxUtils';
@@ -116,7 +117,7 @@ const store = createStore(
   rootReducer,
   initialState,
   compose(
-    applyMiddleware(thunkMiddleware),
+    applyMiddleware(thunkMiddleware, logger),
     initEnhancer(
       !isFeatureEnabled(FeatureFlag.SQLLAB_BACKEND_PERSISTENCE),
       sqlLabPersistStateConfig,
diff --git a/superset-frontend/src/SqlLab/components/App/App.test.jsx 
b/superset-frontend/src/SqlLab/components/App/App.test.jsx
index 0629de27d5..c062629156 100644
--- a/superset-frontend/src/SqlLab/components/App/App.test.jsx
+++ b/superset-frontend/src/SqlLab/components/App/App.test.jsx
@@ -19,21 +19,29 @@
 import React from 'react';
 import configureStore from 'redux-mock-store';
 import thunk from 'redux-thunk';
-
-import { shallow } from 'enzyme';
+import { render } from 'spec/helpers/testing-library';
 
 import App from 'src/SqlLab/components/App';
-import TabbedSqlEditors from 'src/SqlLab/components/TabbedSqlEditors';
 import sqlLabReducer from 'src/SqlLab/reducers/index';
+import { LOCALSTORAGE_MAX_USAGE_KB } from 'src/SqlLab/constants';
+import { LOG_EVENT } from 'src/logger/actions';
+
+jest.mock('src/SqlLab/components/TabbedSqlEditors', () => () => (
+  <div data-test="mock-tabbed-sql-editors" />
+));
+jest.mock('src/SqlLab/components/QueryAutoRefresh', () => () => (
+  <div data-test="mock-query-auto-refresh" />
+));
 
 describe('SqlLab App', () => {
   const middlewares = [thunk];
   const mockStore = configureStore(middlewares);
   const store = mockStore(sqlLabReducer(undefined, {}), {});
-  let wrapper;
-
   beforeEach(() => {
-    wrapper = shallow(<App store={store} />).dive();
+    jest.useFakeTimers();
+  });
+  afterEach(() => {
+    jest.useRealTimers();
   });
 
   it('is valid', () => {
@@ -41,8 +49,31 @@ describe('SqlLab App', () => {
   });
 
   it('should render', () => {
-    const inner = wrapper.dive();
-    expect(inner.find('.SqlLab')).toHaveLength(1);
-    expect(inner.find(TabbedSqlEditors)).toHaveLength(1);
+    const { getByTestId } = render(<App />, { useRedux: true, store });
+    expect(getByTestId('SqlLabApp')).toBeInTheDocument();
+    expect(getByTestId('mock-tabbed-sql-editors')).toBeInTheDocument();
+  });
+
+  it('logs current usage warning', async () => {
+    const localStorageUsageInKilobytes = LOCALSTORAGE_MAX_USAGE_KB + 10;
+    const storeExceedLocalStorage = mockStore(
+      sqlLabReducer(
+        {
+          localStorageUsageInKilobytes,
+        },
+        {},
+      ),
+    );
+
+    const { rerender } = render(<App />, {
+      useRedux: true,
+      store: storeExceedLocalStorage,
+    });
+    rerender(<App updated />);
+    expect(storeExceedLocalStorage.getActions()).toContainEqual(
+      expect.objectContaining({
+        type: LOG_EVENT,
+      }),
+    );
   });
 });
diff --git a/superset-frontend/src/SqlLab/components/App/index.jsx 
b/superset-frontend/src/SqlLab/components/App/index.jsx
index 47fce27db1..4689f8ec21 100644
--- a/superset-frontend/src/SqlLab/components/App/index.jsx
+++ b/superset-frontend/src/SqlLab/components/App/index.jsx
@@ -29,6 +29,8 @@ import {
   LOCALSTORAGE_WARNING_MESSAGE_THROTTLE_MS,
 } from 'src/SqlLab/constants';
 import * as Actions from 'src/SqlLab/actions/sqlLab';
+import { logEvent } from 'src/logger/actions';
+import { LOG_ACTIONS_SQLLAB_WARN_LOCAL_STORAGE_USAGE } from 
'src/logger/LogUtils';
 import TabbedSqlEditors from '../TabbedSqlEditors';
 import QueryAutoRefresh from '../QueryAutoRefresh';
 
@@ -125,6 +127,7 @@ class App extends React.PureComponent {
     ) {
       this.showLocalStorageUsageWarning(
         this.props.localStorageUsageInKilobytes,
+        this.props.queries?.lenghth || 0,
       );
     }
   }
@@ -140,7 +143,7 @@ class App extends React.PureComponent {
     this.setState({ hash: window.location.hash });
   }
 
-  showLocalStorageUsageWarning(currentUsage) {
+  showLocalStorageUsageWarning(currentUsage, queryCount) {
     this.props.actions.addDangerToast(
       t(
         "SQL Lab uses your browser's local storage to store queries and 
results." +
@@ -154,6 +157,14 @@ class App extends React.PureComponent {
         },
       ),
     );
+    const eventData = {
+      current_usage: currentUsage,
+      query_count: queryCount,
+    };
+    this.props.actions.logEvent(
+      LOG_ACTIONS_SQLLAB_WARN_LOCAL_STORAGE_USAGE,
+      eventData,
+    );
   }
 
   render() {
@@ -162,7 +173,7 @@ class App extends React.PureComponent {
       return window.location.replace('/superset/sqllab/history/');
     }
     return (
-      <SqlLabStyles className="App SqlLab">
+      <SqlLabStyles data-test="SqlLabApp" className="App SqlLab">
         <QueryAutoRefresh
           queries={queries}
           refreshQueries={actions?.refreshQueries}
@@ -193,7 +204,7 @@ function mapStateToProps(state) {
 
 function mapDispatchToProps(dispatch) {
   return {
-    actions: bindActionCreators(Actions, dispatch),
+    actions: bindActionCreators({ ...Actions, logEvent }, dispatch),
   };
 }
 
diff --git a/superset-frontend/src/logger/LogUtils.ts 
b/superset-frontend/src/logger/LogUtils.ts
index c6b3545c37..5c4c05e4f0 100644
--- a/superset-frontend/src/logger/LogUtils.ts
+++ b/superset-frontend/src/logger/LogUtils.ts
@@ -50,6 +50,8 @@ export const LOG_ACTIONS_CONFIRM_OVERWRITE_DASHBOARD_METADATA 
=
 export const LOG_ACTIONS_DASHBOARD_DOWNLOAD_AS_IMAGE =
   'dashboard_download_as_image';
 export const LOG_ACTIONS_CHART_DOWNLOAD_AS_IMAGE = 'chart_download_as_image';
+export const LOG_ACTIONS_SQLLAB_WARN_LOCAL_STORAGE_USAGE =
+  'sqllab_warn_local_storage_usage';
 
 // Log event types 
--------------------------------------------------------------
 export const LOG_EVENT_TYPE_TIMING = new Set([
diff --git a/superset-frontend/src/middleware/loggerMiddleware.js 
b/superset-frontend/src/middleware/loggerMiddleware.js
index 3797609d0e..475c1784cc 100644
--- a/superset-frontend/src/middleware/loggerMiddleware.js
+++ b/superset-frontend/src/middleware/loggerMiddleware.js
@@ -72,7 +72,7 @@ const loggerMiddleware = store => next => action => {
     return next(action);
   }
 
-  const { dashboardInfo, explore, impressionId, dashboardLayout } =
+  const { dashboardInfo, explore, impressionId, dashboardLayout, sqlLab } =
     store.getState();
   let logMetadata = {
     impression_id: impressionId,
@@ -90,6 +90,16 @@ const loggerMiddleware = store => next => action => {
       source_id: explore.slice ? explore.slice.slice_id : 0,
       ...logMetadata,
     };
+  } else if (sqlLab) {
+    const editor = sqlLab.queryEditors.find(
+      ({ id }) => id === sqlLab.tabHistory.slice(-1)[0],
+    );
+    logMetadata = {
+      source: 'sqlLab',
+      source_id: editor?.id,
+      db_id: editor?.dbId,
+      schema: editor?.schema,
+    };
   }
 
   const { eventName } = action.payload;

Reply via email to