|
| 1 | +import Ember from 'ember'; |
| 2 | +import md5 from 'npm:js-md5'; |
| 3 | +import _get from 'npm:lodash/get'; |
| 4 | +import Cookie from 'npm:js-cookie'; |
| 5 | +import config from 'ember-get-config'; |
| 6 | +import keenTracking from 'npm:keen-tracking'; |
| 7 | +import BaseAdapter from 'ember-metrics/metrics-adapters/base'; |
| 8 | + |
| 9 | +export default BaseAdapter.extend({ |
| 10 | + session: Ember.inject.service(), |
| 11 | + |
| 12 | + toStringExtension() { |
| 13 | + return 'Keen'; |
| 14 | + }, |
| 15 | + |
| 16 | + init() {}, |
| 17 | + |
| 18 | + trackEvent(properties, node) { |
| 19 | + window.contextVars = {}; |
| 20 | + window.contextVars.currentUser = this.userContextVars(); |
| 21 | + window.contextVars.node = this.nodeContextVars(node); |
| 22 | + return this.KeenTracker().getInstance().trackPrivateEvent('front-end-events', { interaction: properties }, node); |
| 23 | + }, |
| 24 | + |
| 25 | + trackPage(properties) { |
| 26 | + window.contextVars = {}; |
| 27 | + window.contextVars.currentUser = this.userContextVars(); |
| 28 | + return this.KeenTracker().getInstance().trackPageView({ pageViewed: properties }); |
| 29 | + }, |
| 30 | + |
| 31 | + // Use when tracking something specific, not a generic front-end-event. Usually for something we want |
| 32 | + // to isolate and later display to the user. |
| 33 | + trackSpecificCollection(trackingInfo) { |
| 34 | + // Tracking info should be structured like this: |
| 35 | + // {collection: 'keen-collection-name', eventData: eventData, node: node } |
| 36 | + const collection = trackingInfo.collection || 'front-end-events'; |
| 37 | + const properties = trackingInfo.eventData || {}; |
| 38 | + const node = trackingInfo.node || null; |
| 39 | + window.contextVars = {}; |
| 40 | + window.contextVars.currentUser = this.userContextVars(); |
| 41 | + window.contextVars.node = this.nodeContextVars(node); |
| 42 | + return this.KeenTracker().getInstance().trackPrivateEvent(collection, properties, node); |
| 43 | + }, |
| 44 | + |
| 45 | + willDestroy() {}, |
| 46 | + |
| 47 | + // Adapted from osf.io/website/static/js/keen.js |
| 48 | + KeenTracker() { |
| 49 | + function _nowUTC() { |
| 50 | + var now = new Date(); |
| 51 | + return new Date( |
| 52 | + now.getUTCFullYear(), now.getUTCMonth(), now.getUTCDate(), |
| 53 | + now.getUTCHours(), now.getUTCMinutes(), now.getUTCSeconds() |
| 54 | + ); |
| 55 | + } |
| 56 | + |
| 57 | + function _createOrUpdateKeenSession() { |
| 58 | + var expDate = new Date(); |
| 59 | + var expiresInMinutes = 25; |
| 60 | + expDate.setTime(expDate.getTime() + (expiresInMinutes * 60 * 1000)); |
| 61 | + var currentSessionId = Cookie.get('keenSessionId') || keenTracking.helpers.getUniqueId(); |
| 62 | + Cookie.set('keenSessionId', currentSessionId, { |
| 63 | + expires: expDate, path: '/' |
| 64 | + }); |
| 65 | + } |
| 66 | + |
| 67 | + function _getOrCreateKeenId() { |
| 68 | + if (!Cookie.get('keenUserId')) { |
| 69 | + Cookie.set('keenUserId', keenTracking.helpers.getUniqueId(), { |
| 70 | + expires: 365, path: '/' |
| 71 | + }); |
| 72 | + } |
| 73 | + return Cookie.get('keenUserId'); |
| 74 | + } |
| 75 | + |
| 76 | + function _defaultKeenPayload() { |
| 77 | + _createOrUpdateKeenSession(); |
| 78 | + |
| 79 | + var user = window.contextVars.currentUser; |
| 80 | + var node = window.contextVars.node; |
| 81 | + var pageMeta = _get(window, 'contextVars.analyticsMeta.pageMeta', {}); // Do not have this information. |
| 82 | + return { |
| 83 | + page: { |
| 84 | + title: document.title, |
| 85 | + url: document.URL, |
| 86 | + meta: pageMeta, |
| 87 | + info: {}, |
| 88 | + }, |
| 89 | + referrer: { |
| 90 | + url: document.referrer, |
| 91 | + info: {}, |
| 92 | + }, |
| 93 | + time: { |
| 94 | + local: keenTracking.helpers.getDatetimeIndex(), |
| 95 | + utc: keenTracking.helpers.getDatetimeIndex(_nowUTC()), |
| 96 | + }, |
| 97 | + node: { |
| 98 | + id: _get(node, 'id'), |
| 99 | + title: _get(node, 'title'), |
| 100 | + type: _get(node, 'category'), |
| 101 | + tags: _get(node, 'tags'), |
| 102 | + }, |
| 103 | + anon: { |
| 104 | + id: md5(Cookie.get('keenSessionId')), |
| 105 | + continent: user.anon.continent, |
| 106 | + country: user.anon.country, |
| 107 | + }, |
| 108 | + meta: { |
| 109 | + epoch: 1, // version of pageview event schema |
| 110 | + }, |
| 111 | + keen: { |
| 112 | + addons: [ |
| 113 | + { |
| 114 | + name: 'keen:url_parser', |
| 115 | + input: { |
| 116 | + url: 'page.url', |
| 117 | + }, |
| 118 | + output: 'page.info', |
| 119 | + }, |
| 120 | + { |
| 121 | + name: 'keen:url_parser', |
| 122 | + input: { |
| 123 | + url: 'referrer.url', |
| 124 | + }, |
| 125 | + output: 'referrer.info', |
| 126 | + }, |
| 127 | + { |
| 128 | + name: 'keen:referrer_parser', |
| 129 | + input: { |
| 130 | + referrer_url: 'referrer.url', |
| 131 | + page_url: 'page.url', |
| 132 | + }, |
| 133 | + output: 'referrer.info', |
| 134 | + }, |
| 135 | + ] |
| 136 | + }, |
| 137 | + }; |
| 138 | + } // end _defaultKeenPayload |
| 139 | + |
| 140 | + function _trackCustomEvent(client, collection, eventData) { |
| 141 | + if (client === null) { |
| 142 | + return; |
| 143 | + } |
| 144 | + client.recordEvent(collection, eventData); |
| 145 | + } |
| 146 | + |
| 147 | + function _trackCustomEvents(client, events) { |
| 148 | + if (client === null) { |
| 149 | + return; |
| 150 | + } |
| 151 | + client.recordEvents(events); |
| 152 | + } |
| 153 | + |
| 154 | + function KeenTracker() { |
| 155 | + if (instance) { |
| 156 | + throw new Error('Cannot instantiate another KeenTracker instance.'); |
| 157 | + } else { |
| 158 | + var _this = this; |
| 159 | + |
| 160 | + _this._publicClient = null; |
| 161 | + _this._privateClient = null; |
| 162 | + |
| 163 | + _this.init = function _initKeentracker(params) { |
| 164 | + var _this = this; |
| 165 | + |
| 166 | + if (params === undefined) { |
| 167 | + return _this; |
| 168 | + } |
| 169 | + |
| 170 | + _this._publicClient = keenTracking({ |
| 171 | + projectId: params.public.projectId, |
| 172 | + writeKey: params.public.writeKey, |
| 173 | + }); |
| 174 | + _this._publicClient.extendEvents(_defaultPublicKeenPayload); |
| 175 | + |
| 176 | + _this._privateClient = keenTracking({ |
| 177 | + projectId: params.private.projectId, |
| 178 | + writeKey: params.private.writeKey, |
| 179 | + }); |
| 180 | + _this._privateClient.extendEvents(_defaultPrivateKeenPayload); |
| 181 | + |
| 182 | + return _this; |
| 183 | + }; |
| 184 | + |
| 185 | + var _defaultPublicKeenPayload = function() { return _defaultKeenPayload(); }; |
| 186 | + var _defaultPrivateKeenPayload = function() { |
| 187 | + var payload = _defaultKeenPayload(); |
| 188 | + var user = window.contextVars.currentUser; |
| 189 | + payload.visitor = { |
| 190 | + id: _getOrCreateKeenId(), |
| 191 | + session: Cookie.get('keenSessionId'), |
| 192 | + returning: Boolean(Cookie.get('keenUserId')), |
| 193 | + }; |
| 194 | + payload.tech = { |
| 195 | + browser: keenTracking.helpers.getBrowserProfile(), |
| 196 | + ua: '${keen.user_agent}', |
| 197 | + ip: '${keen.ip}', |
| 198 | + info: {}, |
| 199 | + }; |
| 200 | + payload.user = { |
| 201 | + id: user.id, |
| 202 | + entry_point: user.entryPoint, |
| 203 | + institutions: user.institutions, |
| 204 | + locale: user.locale, |
| 205 | + timezone: user.timezone, |
| 206 | + }; |
| 207 | + payload.keen.addons.push({ |
| 208 | + name: 'keen:ip_to_geo', |
| 209 | + input: { |
| 210 | + ip: 'tech.ip', |
| 211 | + }, |
| 212 | + output: 'geo', |
| 213 | + }); |
| 214 | + payload.keen.addons.push({ |
| 215 | + name: 'keen:ua_parser', |
| 216 | + input: { |
| 217 | + ua_string: 'tech.ua' |
| 218 | + }, |
| 219 | + output: 'tech.info', |
| 220 | + }); |
| 221 | + |
| 222 | + return payload; |
| 223 | + }; |
| 224 | + |
| 225 | + _this.trackPageView = function (data) { |
| 226 | + var _this = this; |
| 227 | + if (_get(window, 'contextVars.node.isPublic', false) && |
| 228 | + _get(window, 'contextVars.analyticsMeta.pageMeta.public', false)) { |
| 229 | + _this.trackPublicEvent('pageviews', data); |
| 230 | + } |
| 231 | + _this.trackPrivateEvent('pageviews', data); |
| 232 | + }; |
| 233 | + |
| 234 | + _this.trackPrivateEvent = function(collection, event) { |
| 235 | + return _trackCustomEvent(_this._privateClient, collection, event); |
| 236 | + }; |
| 237 | + _this.trackPrivateEvents = function(events) { |
| 238 | + return _trackCustomEvents(_this._privateClient, events); |
| 239 | + }; |
| 240 | + |
| 241 | + _this.trackPublicEvent = function(collection, event) { |
| 242 | + return _trackCustomEvent(_this._publicClient, collection, event); |
| 243 | + }; |
| 244 | + _this.trackPublicEvents = function(events) { |
| 245 | + return _trackCustomEvents(_this._publicClient, events); |
| 246 | + }; |
| 247 | + } |
| 248 | + } |
| 249 | + |
| 250 | + var instance = null; |
| 251 | + return { |
| 252 | + getInstance() { |
| 253 | + if (!instance) { |
| 254 | + let configInfo = {}; |
| 255 | + config.metricsAdapters.forEach((adapter) => { |
| 256 | + if (adapter.name === 'Keen') { |
| 257 | + configInfo = adapter.config; |
| 258 | + } |
| 259 | + }); |
| 260 | + instance = new KeenTracker(); |
| 261 | + instance.init(configInfo); |
| 262 | + } |
| 263 | + return instance; |
| 264 | + } |
| 265 | + }; |
| 266 | + }, |
| 267 | + userContextVars() { |
| 268 | + // Extract user variables from session. |
| 269 | + const session = this.get('session'); |
| 270 | + let user = {}; |
| 271 | + if (session.get('isAuthenticated')) { |
| 272 | + let userInfo = session.get('session.authenticated'); |
| 273 | + user = { |
| 274 | + id: userInfo.id, |
| 275 | + entry_point: userInfo.attributes.entry_point, // Don't have the entry point. |
| 276 | + institutions: null, // Don't really want to make an API request to fetch user institutions. |
| 277 | + locale: userInfo.attributes.locale, |
| 278 | + timezone: userInfo.attributes.timezone |
| 279 | + }; |
| 280 | + } |
| 281 | + user.anon = {}; // Do not have this info, but most duplicated in geo. |
| 282 | + return user; |
| 283 | + }, |
| 284 | + nodeContextVars(node) { |
| 285 | + // Extract node variables, if passed in. |
| 286 | + let nodeVars = {}; |
| 287 | + if (node && node.id) { |
| 288 | + nodeVars = { |
| 289 | + id: node.get('id'), |
| 290 | + title: node.get('title'), |
| 291 | + type: node.get('category'), |
| 292 | + tags: node.get('tags'), |
| 293 | + isPublic: node.get('public') |
| 294 | + }; |
| 295 | + } |
| 296 | + return nodeVars; |
| 297 | + }, |
| 298 | +}); |
0 commit comments