Commit 6c82462d authored by Jeremy Jackson's avatar Jeremy Jackson Committed by Jan Provaznik

Changes snowplow to use cookies for sessions

This also restructures how and where the configuration for
Snowplow lives.
parent bbd39021
...@@ -35,6 +35,7 @@ import initPerformanceBar from './performance_bar'; ...@@ -35,6 +35,7 @@ import initPerformanceBar from './performance_bar';
import initSearchAutocomplete from './search_autocomplete'; import initSearchAutocomplete from './search_autocomplete';
import GlFieldErrors from './gl_field_errors'; import GlFieldErrors from './gl_field_errors';
import initUserPopovers from './user_popovers'; import initUserPopovers from './user_popovers';
import { initUserTracking } from './tracking';
import { __ } from './locale'; import { __ } from './locale';
import 'ee_else_ce/main_ee'; import 'ee_else_ce/main_ee';
...@@ -94,6 +95,7 @@ function deferredInitialisation() { ...@@ -94,6 +95,7 @@ function deferredInitialisation() {
initLogoAnimation(); initLogoAnimation();
initUsagePingConsent(); initUsagePingConsent();
initUserPopovers(); initUserPopovers();
initUserTracking();
if (document.querySelector('.search')) initSearchAutocomplete(); if (document.querySelector('.search')) initSearchAutocomplete();
......
import $ from 'jquery'; import $ from 'jquery';
const DEFAULT_SNOWPLOW_OPTIONS = {
namespace: 'gl',
hostname: window.location.hostname,
cookieDomain: window.location.hostname,
appId: '',
userFingerprint: false,
respectDoNotTrack: true,
forceSecureTracker: true,
eventMethod: 'post',
contexts: { webPage: true },
// Page tracking tracks a single event when the page loads.
pageTrackingEnabled: false,
// Activity tracking tracks when a user is still interacting with the page.
// Events like scrolling and mouse movements are used to determine if the
// user has the tab active and is still actively engaging.
activityTrackingEnabled: false,
};
const extractData = (el, opts = {}) => { const extractData = (el, opts = {}) => {
const { trackEvent, trackLabel = '', trackProperty = '' } = el.dataset; const { trackEvent, trackLabel = '', trackProperty = '' } = el.dataset;
let trackValue = el.dataset.trackValue || el.value || ''; let trackValue = el.dataset.trackValue || el.value || '';
...@@ -71,3 +89,13 @@ export default class Tracking { ...@@ -71,3 +89,13 @@ export default class Tracking {
}; };
} }
} }
export function initUserTracking() {
if (!Tracking.enabled()) return;
const opts = Object.assign({}, DEFAULT_SNOWPLOW_OPTIONS, window.snowplowOptions);
window.snowplow('newTracker', opts.namespace, opts.hostname, opts);
if (opts.activityTrackingEnabled) window.snowplow('enableActivityTracking', 30, 30);
if (opts.pageTrackingEnabled) window.snowplow('trackPageView'); // must be after enableActivityTracking
}
...@@ -7,23 +7,4 @@ ...@@ -7,23 +7,4 @@
};p[i].q=p[i].q||[];n=l.createElement(o);g=l.getElementsByTagName(o)[0];n.async=1; };p[i].q=p[i].q||[];n=l.createElement(o);g=l.getElementsByTagName(o)[0];n.async=1;
n.src=w;g.parentNode.insertBefore(n,g)}}(window,document,"script","#{asset_url('snowplow/sp.js')}","snowplow")); n.src=w;g.parentNode.insertBefore(n,g)}}(window,document,"script","#{asset_url('snowplow/sp.js')}","snowplow"));
window.snowplow('newTracker', '#{Gitlab::SnowplowTracker::NAMESPACE}', '#{Gitlab::CurrentSettings.snowplow_collector_hostname}', { window.snowplowOptions = #{Gitlab::Tracking.snowplow_options(@group).to_json}
appId: '#{Gitlab::CurrentSettings.snowplow_site_id}',
cookieDomain: '#{Gitlab::CurrentSettings.snowplow_cookie_domain}',
userFingerprint: false,
respectDoNotTrack: true,
forceSecureTracker: true,
post: true,
contexts: { webPage: true },
stateStorageStrategy: "localStorage"
});
window.snowplow('enableActivityTracking', 30, 30);
window.snowplow('trackPageView');
- return unless Feature.enabled?(:additional_snowplow_tracking, @group)
= javascript_tag nonce: true do
:plain
window.snowplow('enableFormTracking');
window.snowplow('enableLinkClickTracking');
# frozen_string_literal: true
require 'snowplow-tracker'
module Gitlab
module SnowplowTracker
NAMESPACE = 'cf'
class << self
def track_event(category, action, label: nil, property: nil, value: nil, context: nil)
tracker&.track_struct_event(category, action, label, property, value, context, Time.now.to_i)
end
private
def tracker
return unless enabled?
@tracker ||= ::SnowplowTracker::Tracker.new(emitter, subject, NAMESPACE, Gitlab::CurrentSettings.snowplow_site_id)
end
def subject
::SnowplowTracker::Subject.new
end
def emitter
::SnowplowTracker::Emitter.new(Gitlab::CurrentSettings.snowplow_collector_hostname)
end
def enabled?
Gitlab::CurrentSettings.snowplow_enabled?
end
end
end
end
# frozen_string_literal: true
require 'snowplow-tracker'
module Gitlab
module Tracking
SNOWPLOW_NAMESPACE = 'gl'
class << self
def enabled?
Gitlab::CurrentSettings.snowplow_enabled?
end
def event(category, action, label: nil, property: nil, value: nil, context: nil)
return unless enabled?
snowplow.track_struct_event(category, action, label, property, value, context, Time.now.to_i)
end
def snowplow_options(group)
additional_features = Feature.enabled?(:additional_snowplow_tracking, group)
{
namespace: SNOWPLOW_NAMESPACE,
hostname: Gitlab::CurrentSettings.snowplow_collector_hostname,
cookie_domain: Gitlab::CurrentSettings.snowplow_cookie_domain,
app_id: Gitlab::CurrentSettings.snowplow_site_id,
page_tracking_enabled: additional_features,
activity_tracking_enabled: additional_features
}.transform_keys! { |key| key.to_s.camelize(:lower).to_sym }
end
private
def snowplow
@snowplow ||= SnowplowTracker::Tracker.new(
SnowplowTracker::Emitter.new(Gitlab::CurrentSettings.snowplow_collector_hostname),
SnowplowTracker::Subject.new,
SNOWPLOW_NAMESPACE,
Gitlab::CurrentSettings.snowplow_site_id
)
end
end
end
end
import $ from 'jquery'; import $ from 'jquery';
import { setHTMLFixture } from './helpers/fixtures'; import { setHTMLFixture } from './helpers/fixtures';
import Tracking from '~/tracking'; import Tracking, { initUserTracking } from '~/tracking';
describe('Tracking', () => { describe('Tracking', () => {
let snowplowSpy;
beforeEach(() => { beforeEach(() => {
window.snowplow = window.snowplow || (() => {}); window.snowplow = window.snowplow || (() => {});
window.snowplowOptions = {
namespace: '_namespace_',
hostname: 'app.gitfoo.com',
cookieDomain: '.gitfoo.com',
};
snowplowSpy = jest.spyOn(window, 'snowplow');
}); });
describe('.event', () => { describe('initUserTracking', () => {
let snowplowSpy = null; it('calls through to get a new tracker with the expected options', () => {
initUserTracking();
expect(snowplowSpy).toHaveBeenCalledWith('newTracker', '_namespace_', 'app.gitfoo.com', {
namespace: '_namespace_',
hostname: 'app.gitfoo.com',
cookieDomain: '.gitfoo.com',
appId: '',
userFingerprint: false,
respectDoNotTrack: true,
forceSecureTracker: true,
eventMethod: 'post',
contexts: { webPage: true },
activityTrackingEnabled: false,
pageTrackingEnabled: false,
});
});
beforeEach(() => { it('should activate features based on what has been enabled', () => {
snowplowSpy = jest.spyOn(window, 'snowplow'); initUserTracking();
expect(snowplowSpy).not.toHaveBeenCalledWith('enableActivityTracking', 30, 30);
expect(snowplowSpy).not.toHaveBeenCalledWith('trackPageView');
window.snowplowOptions = Object.assign({}, window.snowplowOptions, {
activityTrackingEnabled: true,
pageTrackingEnabled: true,
});
initUserTracking();
expect(snowplowSpy).toHaveBeenCalledWith('enableActivityTracking', 30, 30);
expect(snowplowSpy).toHaveBeenCalledWith('trackPageView');
});
}); });
describe('.event', () => {
afterEach(() => { afterEach(() => {
window.doNotTrack = undefined; window.doNotTrack = undefined;
navigator.doNotTrack = undefined; navigator.doNotTrack = undefined;
......
# frozen_string_literal: true
require 'spec_helper'
describe Gitlab::SnowplowTracker do
let(:timestamp) { Time.utc(2017, 3, 22) }
around do |example|
Timecop.freeze(timestamp) { example.run }
end
subject { described_class.track_event('epics', 'action', property: 'what', value: 'doit') }
context '.track_event' do
context 'when Snowplow tracker is disabled' do
it 'does not track the event' do
expect(SnowplowTracker::Tracker).not_to receive(:new)
subject
end
end
context 'when Snowplow tracker is enabled' do
before do
stub_application_setting(snowplow_enabled: true)
stub_application_setting(snowplow_site_id: 'awesome gitlab')
stub_application_setting(snowplow_collector_hostname: 'url.com')
end
it 'tracks the event' do
tracker = double
expect(::SnowplowTracker::Tracker).to receive(:new)
.with(
an_instance_of(::SnowplowTracker::Emitter),
an_instance_of(::SnowplowTracker::Subject),
'cf', 'awesome gitlab'
).and_return(tracker)
expect(tracker).to receive(:track_struct_event)
.with('epics', 'action', nil, 'what', 'doit', nil, timestamp.to_i)
subject
end
end
end
end
# frozen_string_literal: true
require 'spec_helper'
describe Gitlab::Tracking do
let(:timestamp) { Time.utc(2017, 3, 22) }
before do
stub_application_setting(snowplow_enabled: true)
stub_application_setting(snowplow_collector_hostname: 'gitfoo.com')
stub_application_setting(snowplow_cookie_domain: '.gitfoo.com')
stub_application_setting(snowplow_site_id: '_abc123_')
end
describe '.snowplow_options' do
subject(&method(:described_class))
it 'returns useful client options' do
expect(subject.snowplow_options(nil)).to eq(
namespace: 'gl',
hostname: 'gitfoo.com',
cookieDomain: '.gitfoo.com',
appId: '_abc123_',
pageTrackingEnabled: true,
activityTrackingEnabled: true
)
end
it 'enables features using feature flags' do
stub_feature_flags(additional_snowplow_tracking: true)
allow(Feature).to receive(:enabled?).with(
:additional_snowplow_tracking,
'_group_'
).and_return(false)
expect(subject.snowplow_options('_group_')).to include(
pageTrackingEnabled: false,
activityTrackingEnabled: false
)
end
end
describe '.event' do
subject(&method(:described_class))
around do |example|
Timecop.freeze(timestamp) { example.run }
end
it 'can track events' do
tracker = double
expect(SnowplowTracker::Emitter).to receive(:new).with(
'gitfoo.com'
).and_return('_emitter_')
expect(SnowplowTracker::Tracker).to receive(:new).with(
'_emitter_',
an_instance_of(SnowplowTracker::Subject),
'gl',
'_abc123_'
).and_return(tracker)
expect(tracker).to receive(:track_struct_event).with(
'category',
'action',
'_label_',
'_property_',
'_value_',
'_context_',
timestamp.to_i
)
subject.event('category', 'action',
label: '_label_',
property: '_property_',
value: '_value_',
context: '_context_'
)
end
it 'does not track when not enabled' do
stub_application_setting(snowplow_enabled: false)
expect(SnowplowTracker::Tracker).not_to receive(:new)
subject.event('epics', 'action', property: 'what', value: 'doit')
end
end
end
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