Unverified Commit 7da4c54c authored by Marcos Iglesias's avatar Marcos Iglesias Committed by GitHub

feat: Adds page view tracking with the new analytics approach (#838)

* Listens to history from the routes component
Signed-off-by: 's avatarMarcos Iglesias <miglesiasvalle@lyft.com>

* Adds UI reducer, pageView action and logic to track page views using analytics
Signed-off-by: 's avatarMarcos Iglesias <miglesiasvalle@lyft.com>

* Fixing linting error
Signed-off-by: 's avatarMarcos Iglesias <miglesiasvalle@lyft.com>

* Updating betterer results
Signed-off-by: 's avatarMarcos Iglesias <miglesiasvalle@lyft.com>
parent b2d5e8ff
// Copyright Contributors to the Amundsen project.
// SPDX-License-Identifier: Apache-2.0
import { expectSaga, testSaga } from 'redux-saga-test-plan'; import { expectSaga, testSaga } from 'redux-saga-test-plan';
import * as matchers from 'redux-saga-test-plan/matchers'; import * as matchers from 'redux-saga-test-plan/matchers';
import { throwError } from 'redux-saga-test-plan/providers'; import { throwError } from 'redux-saga-test-plan/providers';
......
// Copyright Contributors to the Amundsen project.
// SPDX-License-Identifier: Apache-2.0
import { Middleware } from 'redux'; import { Middleware } from 'redux';
import { RootState } from '../rootReducer'; import { RootState } from '../rootReducer';
......
// Copyright Contributors to the Amundsen project.
// SPDX-License-Identifier: Apache-2.0
import { combineReducers } from 'redux'; import { combineReducers } from 'redux';
import dashboard, { DashboardReducerState } from 'ducks/dashboard/reducer'; import dashboard, { DashboardReducerState } from 'ducks/dashboard/reducer';
...@@ -13,6 +16,7 @@ import tableMetadata, { ...@@ -13,6 +16,7 @@ import tableMetadata, {
import lastIndexed, { LastIndexedReducerState } from './lastIndexed/reducer'; import lastIndexed, { LastIndexedReducerState } from './lastIndexed/reducer';
import tags, { TagsReducerState } from './tags/reducer'; import tags, { TagsReducerState } from './tags/reducer';
import user, { UserReducerState } from './user/reducer'; import user, { UserReducerState } from './user/reducer';
import ui, { UIReducerState } from './ui';
import bookmarks, { BookmarkReducerState } from './bookmark/reducer'; import bookmarks, { BookmarkReducerState } from './bookmark/reducer';
import notification, { NotificationReducerState } from './notification/reducer'; import notification, { NotificationReducerState } from './notification/reducer';
import issue, { IssueReducerState } from './issue/reducer'; import issue, { IssueReducerState } from './issue/reducer';
...@@ -30,6 +34,7 @@ export interface GlobalState { ...@@ -30,6 +34,7 @@ export interface GlobalState {
lastIndexed: LastIndexedReducerState; lastIndexed: LastIndexedReducerState;
tags: TagsReducerState; tags: TagsReducerState;
user: UserReducerState; user: UserReducerState;
ui: UIReducerState;
} }
const rootReducer = combineReducers<GlobalState>({ const rootReducer = combineReducers<GlobalState>({
...@@ -45,6 +50,7 @@ const rootReducer = combineReducers<GlobalState>({ ...@@ -45,6 +50,7 @@ const rootReducer = combineReducers<GlobalState>({
lastIndexed, lastIndexed,
tags, tags,
user, user,
ui,
}); });
export default rootReducer; export default rootReducer;
......
// Copyright Contributors to the Amundsen project.
// SPDX-License-Identifier: Apache-2.0
import reducer, { pageViewed, pageViewActionType, UIReducerState } from '.';
describe('UI ducks', () => {
describe('actions', () => {
describe('pageViewed', () => {
it('returns the action to track a page view', () => {
const expected = '/testURL';
const action = pageViewed(expected);
const { type, meta } = action;
expect(type).toEqual(pageViewActionType);
expect(meta.analytics.name).toEqual(pageViewActionType);
expect(meta.analytics.payload.category).toEqual('pageView');
expect(meta.analytics.payload.label).toEqual(expected);
});
});
});
describe('reducer', () => {
const testState: UIReducerState = {};
describe('when pageViewed is triggered', () => {
it('should return the existing state', () => {
const expected = testState;
const actual = reducer(testState, pageViewed('/testURL'));
expect(actual).toEqual(expected);
});
});
});
});
// Copyright Contributors to the Amundsen project.
// SPDX-License-Identifier: Apache-2.0
export const pageViewActionType = 'analytics/pageView';
// Actions
export function pageViewed(path: string) {
return {
type: pageViewActionType,
meta: {
analytics: {
name: pageViewActionType,
payload: {
category: 'pageView',
label: `${path}`,
},
},
},
};
}
// Reducer
export interface UIReducerState {}
export const initialState: UIReducerState = {};
export default function reducer(
state: UIReducerState = initialState,
action
): UIReducerState {
switch (action.type) {
case pageViewActionType: {
return state;
}
default:
return state;
}
}
// Copyright Contributors to the Amundsen project.
// SPDX-License-Identifier: Apache-2.0
import { GlobalState } from 'ducks/rootReducer'; import { GlobalState } from 'ducks/rootReducer';
import { ResourceType, SendingState } from 'interfaces'; import { ResourceType, SendingState } from 'interfaces';
...@@ -253,6 +256,7 @@ const globalState: GlobalState = { ...@@ -253,6 +256,7 @@ const globalState: GlobalState = {
}, },
}, },
}, },
ui: {},
}; };
export default globalState; export default globalState;
...@@ -16,6 +16,10 @@ import { analyticsMiddleware } from 'ducks/middlewares'; ...@@ -16,6 +16,10 @@ import { analyticsMiddleware } from 'ducks/middlewares';
import { BrowserHistory } from 'utils/navigationUtils'; import { BrowserHistory } from 'utils/navigationUtils';
import { pageViewed } from 'ducks/ui';
import rootReducer from 'ducks/rootReducer';
import rootSaga from 'ducks/rootSaga';
import DashboardPage from './pages/DashboardPage'; import DashboardPage from './pages/DashboardPage';
import AnnouncementPage from './pages/AnnouncementPage'; import AnnouncementPage from './pages/AnnouncementPage';
import BrowsePage from './pages/BrowsePage'; import BrowsePage from './pages/BrowsePage';
...@@ -29,9 +33,6 @@ import Preloader from './components/Preloader'; ...@@ -29,9 +33,6 @@ import Preloader from './components/Preloader';
import Footer from './features/Footer'; import Footer from './features/Footer';
import NavBar from './features/NavBar'; import NavBar from './features/NavBar';
import rootReducer from './ducks/rootReducer';
import rootSaga from './ducks/rootSaga';
const sagaMiddleware = createSagaMiddleware(); const sagaMiddleware = createSagaMiddleware();
const createStoreWithMiddleware = applyMiddleware( const createStoreWithMiddleware = applyMiddleware(
ReduxPromise, ReduxPromise,
...@@ -42,26 +43,45 @@ const store = createStoreWithMiddleware(rootReducer); ...@@ -42,26 +43,45 @@ const store = createStoreWithMiddleware(rootReducer);
sagaMiddleware.run(rootSaga); sagaMiddleware.run(rootSaga);
const Routes: React.FC = () => {
const history = BrowserHistory;
function trackPageView() {
store.dispatch(pageViewed(window.location.pathname));
}
React.useEffect(() => {
trackPageView(); // To track the first pageview upon load
history.listen(trackPageView); // To track the subsequent pageviews
}, [history]);
return (
<>
<Route component={NavBar} />
<Switch>
<Route path="/announcements" component={AnnouncementPage} />
<Route path="/browse" component={BrowsePage} />
<Route path="/dashboard/:uri" component={DashboardPage} />
<Route path="/search" component={SearchPage} />
<Route
path="/table_detail/:cluster/:database/:schema/:table"
component={TableDetail}
/>
<Route path="/user/:userId" component={ProfilePage} />
<Route path="/404" component={NotFoundPage} />
<Route path="/" component={HomePage} />
</Switch>
</>
);
};
ReactDOM.render( ReactDOM.render(
<DocumentTitle title="Amundsen - Data Discovery Portal"> <DocumentTitle title="Amundsen - Data Discovery Portal">
<Provider store={store}> <Provider store={store}>
<Router history={BrowserHistory}> <Router history={BrowserHistory}>
<div id="main"> <div id="main">
<Preloader /> <Preloader />
<Route component={NavBar} /> <Routes />
<Switch>
<Route path="/announcements" component={AnnouncementPage} />
<Route path="/browse" component={BrowsePage} />
<Route path="/dashboard/:uri" component={DashboardPage} />
<Route path="/search" component={SearchPage} />
<Route
path="/table_detail/:cluster/:database/:schema/:table"
component={TableDetail}
/>
<Route path="/user/:userId" component={ProfilePage} />
<Route path="/404" component={NotFoundPage} />
<Route path="/" component={HomePage} />
</Switch>
<Footer /> <Footer />
</div> </div>
</Router> </Router>
......
// Copyright Contributors to the Amundsen project.
// SPDX-License-Identifier: Apache-2.0
import Analytics, { AnalyticsInstance } from 'analytics'; import Analytics, { AnalyticsInstance } from 'analytics';
import * as ConfigUtils from 'config/config-utils'; import * as ConfigUtils from 'config/config-utils';
import { pageViewActionType } from 'ducks/ui';
let sharedAnalyticsInstance; let sharedAnalyticsInstance;
...@@ -20,8 +24,17 @@ export const analyticsInstance = (): AnalyticsInstance => { ...@@ -20,8 +24,17 @@ export const analyticsInstance = (): AnalyticsInstance => {
return sharedAnalyticsInstance; return sharedAnalyticsInstance;
}; };
export const trackEvent = (eventName: string, properties: Map<string, any>) => { export const trackEvent = (
eventName: string,
properties: Record<string, any>
) => {
const analytics = analyticsInstance(); const analytics = analyticsInstance();
analytics.track(eventName, properties); if (eventName === pageViewActionType) {
analytics.page({
url: properties.label,
});
} else {
analytics.track(eventName, properties);
}
}; };
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment