Skip to content

Commit

Permalink
Integrate GA into search page
Browse files Browse the repository at this point in the history
Signed-off-by: Davit Yeghshatyan <davo@uber.com>
  • Loading branch information
Davit Yeghshatyan committed Jun 21, 2018
1 parent d251d6b commit 6f44fc7
Show file tree
Hide file tree
Showing 6 changed files with 120 additions and 5 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -14,4 +14,5 @@ coverage
.DS_Store
npm-debug.log
.vscode
.idea
yarn-error.log
36 changes: 32 additions & 4 deletions packages/jaeger-ui/src/components/SearchTracePage/SearchForm.js
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,8 @@ import { formatDate, formatTime } from '../../utils/date';
import reduxFormFieldAdapter from '../../utils/redux-form-field-adapter';

import './SearchForm.css';
import { trackEvent } from '../../utils/tracking';
import { CATEGORY_BASE } from './SearchForm.track';

const FormItem = Form.Item;
const Option = Select.Option;
Expand All @@ -40,6 +42,21 @@ const AdaptedInput = reduxFormFieldAdapter(Input);
const AdaptedSelect = reduxFormFieldAdapter(Select);
const AdaptedVirtualSelect = reduxFormFieldAdapter(VirtSelect, option => (option ? option.value : null));

const DEFAULT_OPERATION = 'all';
const DEFAULT_LOOKBACK = '1h';
const DEFAULT_LIMIT = 20;

const ACTION_SET = 'set';
const ACTION_CLEAR = 'clear';
const ACTION_DEFAULT = 'default';

export const CATEGORY_OPERATION = `${CATEGORY_BASE}/operation`;
export const CATEGORY_LOOKBACK = `${CATEGORY_BASE}/lookback`;
export const CATEGORY_TAGS = `${CATEGORY_BASE}/tags`;
export const CATEGORY_MIN_DURATION = `${CATEGORY_BASE}/min_duration`;
export const CATEGORY_MAX_DURATION = `${CATEGORY_BASE}/max_duration`;
export const CATEGORY_LIMIT = `${CATEGORY_BASE}/limit`;

export function getUnixTimeStampInMSFromForm({ startDate, startDateTime, endDate, endDateTime }) {
const start = `${startDate} ${startDateTime}`;
const end = `${endDate} ${endDateTime}`;
Expand Down Expand Up @@ -96,6 +113,15 @@ export function convertQueryParamsToFormDates({ start, end }) {
};
}

function trackFormInput(resultsLimit, operation, tags, minDuration, maxDuration, lookback) {
trackEvent(CATEGORY_OPERATION, operation === DEFAULT_OPERATION ? ACTION_DEFAULT : ACTION_SET, operation);
trackEvent(CATEGORY_LIMIT, resultsLimit === DEFAULT_LIMIT ? ACTION_DEFAULT : ACTION_SET, resultsLimit);
trackEvent(CATEGORY_TAGS, tags ? ACTION_SET : ACTION_CLEAR, tags);
trackEvent(CATEGORY_MAX_DURATION, maxDuration ? ACTION_SET : ACTION_CLEAR, maxDuration);
trackEvent(CATEGORY_MIN_DURATION, minDuration ? ACTION_SET : ACTION_CLEAR, minDuration);
trackEvent(CATEGORY_LOOKBACK, lookback);
}

export function submitForm(fields, searchTraces) {
const {
resultsLimit,
Expand Down Expand Up @@ -134,9 +160,11 @@ export function submitForm(fields, searchTraces) {
end = times.end;
}

trackFormInput(resultsLimit, operation, tags, minDuration, maxDuration, lookback);

searchTraces({
service,
operation: operation !== 'all' ? operation : undefined,
operation: operation !== DEFAULT_OPERATION ? operation : undefined,
limit: resultsLimit,
lookback,
start,
Expand Down Expand Up @@ -459,13 +487,13 @@ export function mapStateToProps(state) {
destroyOnUnmount: false,
initialValues: {
service: service || lastSearchService || '-',
resultsLimit: limit || 20,
lookback: lookback || '1h',
resultsLimit: limit || DEFAULT_LIMIT,
lookback: lookback || DEFAULT_LOOKBACK,
startDate: queryStartDate || today,
startDateTime: queryStartDateTime || '00:00',
endDate: queryEndDate || today,
endDateTime: queryEndDateTime || currentTime,
operation: operation || lastSearchOperation || 'all',
operation: operation || lastSearchOperation || DEFAULT_OPERATION,
tags,
minDuration: minDuration || null,
maxDuration: maxDuration || null,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,12 +14,14 @@

/* eslint-disable import/first */
jest.mock('store');
jest.mock('../../utils/tracking');

import React from 'react';
import { shallow } from 'enzyme';
import moment from 'moment';
import queryString from 'query-string';
import store from 'store';
import { trackEvent } from '../../utils/tracking';

import {
convertQueryParamsToFormDates,
Expand All @@ -29,6 +31,12 @@ import {
submitForm,
traceIDsToQuery,
SearchFormImpl as SearchForm,
CATEGORY_OPERATION,
CATEGORY_LIMIT,
CATEGORY_TAGS,
CATEGORY_MAX_DURATION,
CATEGORY_MIN_DURATION,
CATEGORY_LOOKBACK,
} from './SearchForm';
import * as markers from './SearchForm.markers';

Expand Down Expand Up @@ -139,6 +147,7 @@ describe('submitForm()', () => {
resultsLimit: 20,
service: 'svc-a',
};
trackEvent.mockClear();
});

it('ignores `fields.operation` when it is "all"', () => {
Expand Down Expand Up @@ -240,6 +249,22 @@ describe('submitForm()', () => {
expect(maxDuration).toBe(null);
});
});

it('sends form input to GA', () => {
submitForm(fields, searchTraces);
expect(trackEvent.mock.calls.length).toBe(6);
const categoriesTracked = trackEvent.mock.calls.map(call => call[0]).sort();
expect(categoriesTracked).toEqual(
[
CATEGORY_OPERATION,
CATEGORY_LIMIT,
CATEGORY_TAGS,
CATEGORY_MAX_DURATION,
CATEGORY_MIN_DURATION,
CATEGORY_LOOKBACK,
].sort()
);
});
});

describe('<SearchForm>', () => {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
// @flow

// Copyright (c) 2017 Uber Technologies, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

import type { Store } from 'redux';
import { trackEvent } from '../../utils/tracking';

export const CATEGORY_BASE = 'jaeger/ux/trace/search';
export const CATEGORY_SORTBY = `${CATEGORY_BASE}/sortby`;
export const FORM_CHANGE_ACTION_TYPE = '@@redux-form/CHANGE';

export const middlewareHooks = {
[FORM_CHANGE_ACTION_TYPE]: (store: Store, action: any) => {
if (action.meta.form === 'sortBy') {
trackEvent(CATEGORY_SORTBY, action.payload);
}
},
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
// Copyright (c) 2017 Uber Technologies, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

/* eslint-disable import/first */
jest.mock('../../utils/tracking');

import { trackEvent } from '../../utils/tracking';
import * as track from '../SearchTracePage/SearchForm.track';

describe('middlewareHooks', () => {
it('tracks a GA event for changing sort criteria', () => {
const action = { meta: { form: 'sortBy' }, payload: 'MOST_RECENT' };
track.middlewareHooks[track.FORM_CHANGE_ACTION_TYPE]({}, action);
expect(trackEvent.mock.calls.length).toBe(1);
expect(trackEvent.mock.calls[0]).toEqual([track.CATEGORY_SORTBY, expect.any(String)]);
});
});
5 changes: 4 additions & 1 deletion packages/jaeger-ui/src/middlewares/track.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,12 @@
// See the License for the specific language governing permissions and
// limitations under the License.

import { middlewareHooks } from '../components/TracePage/TraceTimelineViewer/duck.track';
import { middlewareHooks as timelineHooks } from '../components/TracePage/TraceTimelineViewer/duck.track';
import { middlewareHooks as searchHooks } from '../components/SearchTracePage/SearchForm.track';
import { isGaEnabled } from '../utils/tracking';

const middlewareHooks = { ...timelineHooks, ...searchHooks };

function trackingMiddleware(store: { getState: () => any }) {
return function inner(next: any => void) {
return function core(action: any) {
Expand Down

0 comments on commit 6f44fc7

Please sign in to comment.