___TERMS_OF_SERVICE___ By creating or modifying this file you agree to Google Tag Manager's Community Template Gallery Developer Terms of Service available at https://developers.google.com/tag-manager/gallery-tos (or such other URL as Google may provide), as modified from time to time. ___INFO___ { "type": "CLIENT", "id": "cvt_temp_public_id", "__wm": "VGVtcGxhdGUtQXV0aG9yX0FkdmFuY2VkVW5pdmVyc2FsQW5hbHl0aWNzLVNpbW8tQWhhdmE\u003d", "categories": [ "ANALYTICS" ], "version": 1, "securityGroups": [], "displayName": "Piwik PRO Client", "brand": { "id": "brand_dummy", "displayName": "", "thumbnail": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAMgAAADICAYAAACtWK6eAAAACXBIWXMAAA7EAAAOxAGVKw4bAAAI70lEQVR42u3dW0xUdwLH8d+ZYYTBG6PVMRijKC4Ub8FmsTFmq3Txvn3YWtON+qLQtdLtproPlgdsrNuXFrP1jt2UiCYmpX1wUzcqIuKleGNFDdYuipdojYRRmUFh7vtgdmMMXrZy0MP5fhJfz/H85//lzDlzLsbItLS4AHTKwRAABAIQCEAgAIEABAIQCEAgAIEABAKAQAACAQgEIBCAQAACAQgEIBCAQAACAUAgAIEABAIQCEAgAIEABAIQCEAgAAgEIBCAQAACAQgEIBCAQAACAQgEIBAABAIQCEAgAIEABAIQCEAgAIEABAKAQAACAQgEMEcCQ/DIXwyHQ263W4mJiXI4nTJeov9bNBpVJBJROBxWOBxWJBLhAyOQ7tG/f3/l5eUpNzdXGZmZGjx4sNxutwzj5Umkvb1dfr9ffr9fPp9P165d08XGRjU0NKixsVE+n48PsosZI9PS4nYegMTERL0zf74KCgo0dOjQlyqIZxWJRBQIBPTj+fOqrq5WVVWVbty4wR6GQJ7PwIED9dfPPlNeXl6P2q5gMKiTJ06ooqJC1dXVunfvHjOdQP4/Ho9HpaWlmvjaa5bcazyLWCymq1evqqysTP/YtUuBQIAZTyBP53K5VLJ2rebMmWObbb506ZLWr1unPXv2KBwOM/OfkS1P886ePVszZsyw1TaPGjVKX5SUaEtpqUakpTHzCaRzbrdbf1y6VAkJ9juBl5CQoKlTp6qiokLvzJ8vh4OfwZ7G6fF4PrHTBudMmqT8/Pwee9zxrH8kpk6dqlcGDtSpU6cUCoUogT3IA7nTptk6jof3JgsXLdLGTZv0yqBBlEAgD4wZM4ZP/SFTpkzR1q1blTp0KINBIJJ3yBA+9UdMmDBBpaWl8nq9DIbdA7HjwfmzyMrK0t++/FIpKSkMhp0DwePl5OTo0zVr1KtXLwaDQNCZWbNmafGSJZzIIBB0xjAMFRYWatLrrzMYBILOJCcnq7i4WH379iUQpgM6k5GRoYL33iMQpgIeZ8GCBUpPTycQoDMpKSnKLyiw9TVbBNKFYrFYl/+Lx1/s3Qhz587ViBEjbPuZ8qtZFwmFQlq2bFmX3uZqSHInJys1NVVZr76q7IkTNXz48G79i+52u7Vw4UKtXr2aQPB8e4+jR46YemVsv379NH78eC1eskSTJ0+Wy+Xqlm2bMXOmNmzYoNu3b/MVCy8vv9+vI0eOaMnixSrIz9fFxsZuWa/X69Vv3niDYxBYQzwe1+HDh7Vo0SJVVlaafpxiGIamT59uy4N1ArGw5uZm/fnDD7X7++9NX1dOTo769etHILCWYDCo4uJinTx50tT1eDweZWVlEQisp7W1VUUffyy/32/qen6dk0MgsKampiZt27bN1HWMHTuWQGBdO3fuVHNzs2nLT09Pl9PpJBBY9KD91i0dOnTItOX36dNHAwYMIBBYUzweV/WBA6ad9k1KSpKHQGBlDQ0Npj1a1OVyqU+fPgQC67p586Zprz1wOp1KSkwkEFhXNBrV/fv3zZksDoecNnsqDIH0QGY9cCEejysWixEIrCspKUm9e/c2ZdmxWExhmz3Hl0B6mLS0NNMejhcOh233tioC6WGys7NNCyQYDKq1tZVAYE0JCQmaMXOmactvb29XS0sLgcCaMjIzNXHiRNOWf/36dXV0dBAILPhBOhz60wcfyO12m7aOhoYG+40rU6tnmDFzpnLffNPUddSfPk0gsJ6sMWO0atUqU6+0DYVCpt+URSDocunp6Vq/fr0GmfwatYaGBvl8PgKBNRiGod/m5al8+/ZuebBbZWWlLV/2yXOxLBiG1+tVYWGhfv/220pKSjJ9nW1tbTpQVWXL8SYQK+zmHQ4NGDBAv8rI0OxZs/S7t97q1svOT544oYsXLxIIfjmXy6VP16zp0ov5DMNQ7969lZqaqiFerwZ7vd3+bKp4PK7y8vIX/oxgArE4p9OpefPm9bjtOnXqlI4dO2bfvTdTG48TDof196++suXBOYHgqWpqalRdXW3v4z+mATrT1tamLz7/XNFolECAh8ViMa1ft862Z64IBE90qKZGO3bssO2ZKwLBY125ckVFRUW2u6ydQPBUd+7c0V9WrNCtW7cYDALBwwKBgJZ/9JFO2/CSdgLBE7W1tWnlypU6fPgwg/EIfkm3OZ/PpxXLlxMHexA8qqmpSQX5+cRBIHhYNBrV3r179Yd339WZM2cYEL5i4b/8fr9KSkpU8c03CgaDDAiBQHpwT3nlvn0qKSnR1atXGRACgfTgfo7a2lqVlpaq9ocfbH9tFYHgf1+ljh49qu3l5aqrqzPtnSEEAssIh8M6e/as9u3bpwNVVbp8+TLXUxGI/cSiUd1vb1cgEFBzc7POnj2rf9XV6fjx42ppaeFrFIG8fCKRiNauXWvq5Az4/WppadHdu3d19+5dtfh88re2spcgEGsEUvb117a+PbUn4odCgEAAAgEIBCAQgEAAAgEIBCAQgEAAEAhAIACBAAQCEAhAIC9e0KSnlkciEe77JhDru/Hzz6Ys9+bNm9zZRyDWV19fb8pyz5w5QyAEYn1V+/d36bvMpQfPnqrav5/ZRCDWd+HCBR0/frxLl/nvn35SbW0ts4lArC8cDmvTxo1d9oqxWCymzVu2KBAIMJsIpGeora1VWVnZcx8zxONx7dq1S//cvZuZ1EM5PR7PJ3bc8Lq6OqWmpiozM1OGYfyiZRysruaFlwTSM0WjUR2qqZHD4dC4ceOUkPDsjwgLhUL67ttvVVRUpLa2NmZRD2aMTEuz9blJwzA0adIkLX3/fU2ePFlOp/OJxxv19fXavHmzag4e5BGfBGIfLpdLo0eP1rTcXGVnZ2vYsGFKTk5WR0eHbly/rnPnzqnqwAH9eP48L54hEAASFysCBAIQCEAgAIEABAIQCEAgAIEABAKAQAACAQgEIBCAQAACAQgEIBCAQAACYQgAAgEIBCAQgEAAAgEIBCAQgEAAAgFAIACBAAQCEAhAIACBAAQCEAhAIACBACAQgEAAAgEIBCAQgEAAAgEIBCAQAAQCEAhAIIBp/gOi0A7MqhZt2QAAAABJRU5ErkJggg\u003d\u003d" }, "description": "Piwik PRO Client template. Use this with the Piwik PRO JavaScript library to generate events for the Piwik PRO server-side Google Tag Manager tag.", "containerContexts": [ "SERVER" ] } ___TEMPLATE_PARAMETERS___ [ { "type": "CHECKBOX", "name": "serveJs", "checkboxText": "Serve requests for ppms.js", "simpleValueType": true, "help": "Allow server-side Google Tag Manager to respond to requests for the ppms.js file, in case you want to load this resource from your endpoint instead of from Piwik PRO\u0027s content distribution network.", "subParams": [ { "type": "TEXT", "name": "instanceName", "displayName": "Account name", "simpleValueType": true, "valueHint": "myinstance", "valueValidators": [ { "type": "NON_EMPTY" } ], "enablingConditions": [ { "paramName": "serveJs", "paramValue": true, "type": "EQUALS" } ], "valueUnit": ".piwik.pro" }, { "type": "TEXT", "name": "allowedOrigins", "displayName": "Allowed origins", "simpleValueType": true, "valueHint": "https://my-origin.com,https://www.other-origin.com", "enablingConditions": [ { "paramName": "serveJs", "paramValue": true, "type": "EQUALS" } ], "valueValidators": [ { "type": "NON_EMPTY" } ], "help": "Specify a list of URL origins from which requests for the JavaScript file can be served. If the request comes from an origin not in this list, it will be ignored. For more information on how to format the URL origin string, \u003ca href\u003d\"https://developer.mozilla.org/en-US/docs/Web/API/URL/origin\"\u003eread this\u003c/a\u003e. Type \u003cstrong\u003e*\u003c/strong\u003e to allow requests from any origin (not recommended!)." }, { "type": "CHECKBOX", "name": "cacheRequests", "checkboxText": "Cache the resource in server-side GTM (recommended)", "simpleValueType": true, "help": "Check this box if you want SGTM to cache the JavaScript library for 12 hours. This reduces network egress costs because the file is loaded from SGTM rather than from over the network.", "enablingConditions": [ { "paramName": "serveJs", "paramValue": true, "type": "EQUALS" } ] } ] } ] ___SANDBOXED_JS_FOR_SERVER___ const claimRequest = require('claimRequest'); const createRegex = require('createRegex'); const decodeUriComponent = require('decodeUriComponent'); const getRemoteAddress = require('getRemoteAddress'); const getRequestBody = require('getRequestBody'); const getRequestHeader = require('getRequestHeader'); const getRequestMethod = require('getRequestMethod'); const getRequestPath = require('getRequestPath'); const getRequestQueryString = require('getRequestQueryString'); const getTimestampMillis = require('getTimestampMillis'); const JSON = require('JSON'); const logToConsole = require('logToConsole'); const Object = require('Object'); const parseUrl = require('parseUrl'); const runContainer = require('runContainer'); const returnResponse = require('returnResponse'); const sendHttpGet = require('sendHttpGet'); const setResponseBody = require('setResponseBody'); const setResponseHeader = require('setResponseHeader'); const setResponseStatus = require('setResponseStatus'); const templateDataStorage = require('templateDataStorage'); const CACHE_MAX_TIME_MS = 43200000; const CDN_PATH = 'https://' + data.instanceName + '.piwik.pro/ppms.js'; const COMMON_EVENT_KEYS_IN_PIWIK = ['event_name', '_id', 'cip', 'lang', 'url', 'urlref', 'res', 'ua', 'uid', 'revenue', 'e_v']; const DEFAULT_EVENT_NAME = 'piwik'; const EVENT_PREFIX = 'x-pp-'; const JSON_FIELDS = ['cvar', '_cvar', 'search_cats', 'ec_products']; const LOG_PREFIX = '[ppms_client] '; const REQUEST_METHOD = getRequestMethod(); const REQUEST_ORIGIN = getRequestHeader('origin') || (!!getRequestHeader('referer') && parseUrl(getRequestHeader('referer')).origin) || null; const REQUEST_PATH = getRequestPath(); const STORED_JS_NAME = 'ppms_js'; const STORED_HEADERS_NAME = STORED_JS_NAME + '_headers'; const STORED_TIMEOUT_NAME = STORED_JS_NAME + '_timeout'; const VALID_REQUEST_METHODS = ['GET', 'POST']; /** * Helper to log messages with the Piwik PRO prefix * * @param {String} msg - the message to be logged. */ const log = msg => { logToConsole(LOG_PREFIX + msg); }; /** * Validates the request origin against the list of allowed origins. * * @returns {Boolean} Whether the origin is valid or not. */ const validateOrigin = () => { return data.allowedOrigins === '*' || data.allowedOrigins.split(',').map(origin => origin.trim()).indexOf(REQUEST_ORIGIN) > -1; }; /** * Returns an object with top-level undefined/null keys removed. * * @param {Object} obj - the object to be cleaned. */ const cleanObject = (obj) => { let target = {}; Object.keys(obj).forEach((k) => { if (obj[k] != null) target[k] = obj[k]; }); return target; }; /** * Returns the path to the ppms.js file. */ const getPpmsFilePath = () => { return '/ppms.js'; }; /** * Returns the path to the ppms.php endpoint. */ const getPpmsEndpointPath = () => { return '/ppms.php'; }; /** * Merges the two objects together by preferencing obj2. * * @param {Object} obj - the object to which the second object is merged. * @param {Object} obj2 - the object that is merged with the first object. * @returns {Object} The merged object. */ const mergeObj = (obj, obj2) => { for (let key in obj2) { if (obj2.hasOwnProperty(key)) obj[key] = obj2[key]; } return obj; }; /** * Converts a URL request string into an object with values decoded. * * @param {String} requestString - the request string that needs to be parsed. * @returns {Object} Object with each request parameter corresponding to a key. */ const requestStringToObj = (requestString) => { return requestString.split('&').reduce((acc, cur) => { const pair = cur.split('='); acc[pair[0]] = decodeUriComponent(pair[1]); return acc; }, {}); }; /** * Generates an event_name value based on the contents of the Piwik PRO request. * * @param {Object} requestData - the request data. * @returns {Object} The requestData object with the event_name property populated. */ const generateEventName = (requestData) => { if (requestData.event_name) { return requestData; } else if (requestData.ping) { requestData.event_name = 'ping'; } else if (requestData.e_t) { requestData.event_name = requestData.e_t; } else if (requestData.e_c) { requestData.event_name = requestData.e_c; } else if (requestData.search) { requestData.event_name = 'search'; } else if (requestData.link) { requestData.event_name = 'click'; } else if (requestData.download) { requestData.event_name = 'file_download'; } else if (requestData.action_name) { requestData.event_name = 'page_view'; } else if (requestData.idgoal) { requestData.event_name = 'goal_conversion'; } else { requestData.event_name = DEFAULT_EVENT_NAME; } return requestData; }; /** * Maps the requestData object to both a common event schema as well as Piwik-specific keys. * * @param {Object} requestData - the request data. * @returns {Object} A merged object with both common event data and Piwik-specific data. */ const mapToEventSchema = (requestData) => { const sourceUrl = parseUrl(requestData.url) || {}; // Common event data from https://developers.google.com/tag-platform/tag-manager/server-side/common-event-data const commonEventData = { event_name: requestData.event_name, client_id: requestData._id, ip_override: requestData.cip || getRemoteAddress(), language: requestData.lang || getRequestHeader('Accept-Language'), page_hostname: sourceUrl.hostname, page_location: requestData.url, page_path: sourceUrl.pathname, page_referrer: requestData.urlref, screen_resolution: requestData.res, user_agent: requestData.ua || getRequestHeader('User-Agent'), user_id: requestData.uid, value: requestData.revenue || requestData.e_v }; // All the rest of the keys can be assumed to be Piwik-specific const piwikData = Object.keys(requestData) .filter(key => COMMON_EVENT_KEYS_IN_PIWIK.indexOf(key) === -1) .reduce((acc, cur) => { // If the value is JSON, parse it into the event data object acc[EVENT_PREFIX + cur] = JSON_FIELDS.indexOf(cur) > -1 ? JSON.parse(requestData[cur]) : requestData[cur]; return acc; }, {}); return mergeObj(commonEventData, piwikData); }; /** * Sets the response body, headers, and status before returning it. */ const sendCDNResponse = (body, headers, statusCode) => { setResponseStatus(statusCode); setResponseBody(body); for (const key in headers) { setResponseHeader(key, headers[key]); } returnResponse(); }; // Check if request is for the ppms.js file if (REQUEST_PATH === getPpmsFilePath() && !!data.serveJs) { if (!validateOrigin()) { log('Request originated from invalid origin'); return; } // Claim the request claimRequest(); log('ppms.js request claimed'); const now = getTimestampMillis(); const storageExpireTime = now - CACHE_MAX_TIME_MS; const storedJsBody = templateDataStorage.getItemCopy(STORED_JS_NAME); const storedHeaders = templateDataStorage.getItemCopy(STORED_HEADERS_NAME); const storedTimeout = templateDataStorage.getItemCopy(STORED_TIMEOUT_NAME); if (!storedJsBody || storedTimeout < storageExpireTime) { log('No cache hit or cache expired, fetching ppms.js over the network.'); sendHttpGet(CDN_PATH, {timeout: 1500}) .then(result => { if (result.statusCode === 200) { templateDataStorage.setItemCopy(STORED_JS_NAME, result.body); templateDataStorage.setItemCopy(STORED_HEADERS_NAME, result.headers); templateDataStorage.setItemCopy(STORED_TIMEOUT_NAME, now); } sendCDNResponse(result.body, result.headers, result.statusCode); }); } else { log('Cache hit successful, fetching ppms.js from SGTM storage.'); sendCDNResponse( storedJsBody, storedHeaders, 200 ); } } // Check if request is a Piwik PRO event if (REQUEST_PATH === getPpmsEndpointPath() && VALID_REQUEST_METHODS.indexOf(REQUEST_METHOD) > -1) { // Claim the request claimRequest(); log('ppms.php request claimed'); const requestString = REQUEST_METHOD === 'GET' ? getRequestQueryString() : getRequestBody(); let requestData = requestStringToObj(requestString); requestData = generateEventName(requestData); requestData = mapToEventSchema(requestData); requestData = cleanObject(requestData); runContainer(requestData, () => returnResponse()); } ___SERVER_PERMISSIONS___ [ { "instance": { "key": { "publicId": "read_request", "versionId": "1" }, "param": [ { "key": "requestAccess", "value": { "type": 1, "string": "any" } }, { "key": "headerAccess", "value": { "type": 1, "string": "any" } }, { "key": "queryParameterAccess", "value": { "type": 1, "string": "any" } } ] }, "clientAnnotations": { "isEditedByUser": true }, "isRequired": true }, { "instance": { "key": { "publicId": "logging", "versionId": "1" }, "param": [ { "key": "environments", "value": { "type": 1, "string": "debug" } } ] }, "clientAnnotations": { "isEditedByUser": true }, "isRequired": true }, { "instance": { "key": { "publicId": "return_response", "versionId": "1" }, "param": [] }, "isRequired": true }, { "instance": { "key": { "publicId": "run_container", "versionId": "1" }, "param": [] }, "isRequired": true }, { "instance": { "key": { "publicId": "access_template_storage", "versionId": "1" }, "param": [] }, "isRequired": true }, { "instance": { "key": { "publicId": "access_response", "versionId": "1" }, "param": [ { "key": "writeResponseAccess", "value": { "type": 1, "string": "any" } }, { "key": "writeHeaderAccess", "value": { "type": 1, "string": "specific" } } ] }, "clientAnnotations": { "isEditedByUser": true }, "isRequired": true }, { "instance": { "key": { "publicId": "send_http", "versionId": "1" }, "param": [ { "key": "allowedUrls", "value": { "type": 1, "string": "specific" } }, { "key": "urls", "value": { "type": 2, "listItem": [ { "type": 1, "string": "https://*.piwik.pro/ppms.js" } ] } } ] }, "clientAnnotations": { "isEditedByUser": true }, "isRequired": true } ] ___TESTS___ scenarios: - name: v3 spjs proxied code: | const mockData = { ipInclude: true, populateGaProps: true, serveSpJs: true, customSpJsName: 'example.js', customPostPath: 'custom/path', claimGetRequests: true, includeOriginalTp2Event: true, includeOriginalSelfDescribingEvent: false, includeOriginalContextsArray: false, defaultUserId: true, defaultClientId: true, mergeEntities: false, mergeSelfDesc: false, }; // mocks mock('getRequestPath', '/3.0.0/sp.js'); const mockCDNHeaders = { 'Content-Type': 'application/javascript', 'Filter-Out': 'foo' }; const expectedHeaders = { 'Content-Type': 'application/javascript' }; let httpGetCallback; mock('sendHttpGet', (url, cb, opts) => { httpGetCallback = cb; cb(200, mockCDNHeaders, 'body'); }); const expectedSpJsKey = 'snowplow_js_3.0.0'; const expectedSpJsHeadersKey = 'snowplow_js_headers_3.0.0'; const prevSpJsStored = storage.getItemCopy(expectedSpJsKey); assertThat(prevSpJsStored).isNull(); runCode(mockData); assertApi('claimRequest').wasCalled(); assertApi('sendHttpGet').wasCalledWith( 'https://cdn.jsdelivr.net/npm/@snowplow/javascript-tracker@3.0.0/dist/sp.js', httpGetCallback, { timeout: 5000 } ); assertApi('setResponseHeader').wasCalledWith( 'Content-Type', 'application/javascript' ); assertApi('setResponseBody').wasCalledWith('body'); assertApi('getRequestHeader').wasCalledWith('origin'); assertApi('setResponseHeader').wasCalledWith( 'Access-Control-Allow-Origin', 'origin' ); assertApi('setResponseHeader').wasCalledWith( 'Access-Control-Allow-Credentials', 'true' ); assertApi('returnResponse').wasCalled(); const nextSpJsStored = storage.getItemCopy(expectedSpJsKey); assertThat(nextSpJsStored).isNotNull(); assertThat(nextSpJsStored).isEqualTo('body'); assertThat(storage.getItemCopy(expectedSpJsHeadersKey)).isEqualTo(expectedHeaders); assertThat(storage.getItemCopy('snowplow_gtm_ss_client_version')).isNotNull(); - name: v2 spjs proxied code: | const mockData = { ipInclude: true, populateGaProps: true, serveSpJs: true, customSpJsName: 'example.js', customPostPath: 'custom/path', claimGetRequests: true, includeOriginalTp2Event: true, includeOriginalSelfDescribingEvent: false, includeOriginalContextsArray: false, defaultUserId: true, defaultClientId: true, mergeEntities: false, mergeSelfDesc: false, }; // mocks mock('getRequestPath', () => '/2.18.0/sp.js'); let httpGetCallback; mock('sendHttpGet', (url, cb, opts) => { httpGetCallback = cb; cb(200, { 'Content-Type': 'application/javascript', 'filter-out': 'foo' }, 'body'); }); runCode(mockData); assertApi('claimRequest').wasCalled(); assertApi('sendHttpGet').wasCalledWith( 'https://cdn.jsdelivr.net/gh/snowplow/sp-js-assets@2.18.0/sp.js', httpGetCallback, { timeout: 5000 } ); assertApi('setResponseHeader').wasCalledWith('Content-Type', 'application/javascript'); assertApi('setResponseBody').wasCalledWith('body'); assertApi('getRequestHeader').wasCalledWith('origin'); assertApi('setResponseHeader').wasCalledWith('Access-Control-Allow-Origin', 'origin'); assertApi('setResponseHeader').wasCalledWith('Access-Control-Allow-Credentials', 'true'); assertApi('returnResponse').wasCalled(); - name: v3 spjs with custom name proxied code: | const mockData = { ipInclude: true, populateGaProps: true, serveSpJs: true, customSpJsName: 'example.js', customPostPath: 'custom/path', claimGetRequests: true, includeOriginalTp2Event: true, includeOriginalSelfDescribingEvent: false, includeOriginalContextsArray: false, defaultUserId: true, defaultClientId: true, mergeEntities: false, mergeSelfDesc: false, }; // mocks mock('getRequestPath', () => { return '/3.1.0/example.js'; }); let httpGetCallback; mock('sendHttpGet', (url, cb, opts) => { httpGetCallback = cb; cb(200, { 'Content-Type': 'application/javascript' }, 'body'); }); runCode(mockData); assertApi('claimRequest').wasCalled(); assertApi('sendHttpGet').wasCalledWith( 'https://cdn.jsdelivr.net/npm/@snowplow/javascript-tracker@3.1.0/dist/sp.js', httpGetCallback, { timeout: 5000 } ); assertApi('setResponseHeader').wasCalledWith( 'Content-Type', 'application/javascript' ); assertApi('setResponseBody').wasCalledWith('body'); assertApi('getRequestHeader').wasCalledWith('origin'); assertApi('setResponseHeader').wasCalledWith( 'Access-Control-Allow-Origin', 'origin' ); assertApi('setResponseHeader').wasCalledWith( 'Access-Control-Allow-Credentials', 'true' ); assertApi('returnResponse').wasCalled(); - name: v2 spjs with custom name proxied code: | const mockData = { ipInclude: true, populateGaProps: true, serveSpJs: true, customSpJsName: 'example.js', customPostPath: 'custom/path', claimGetRequests: true, includeOriginalTp2Event: true, includeOriginalSelfDescribingEvent: false, includeOriginalContextsArray: false, defaultUserId: true, defaultClientId: true, mergeEntities: false, mergeSelfDesc: false, }; // mocks mock('getRequestPath', () => { return '/2.18.1/example.js'; }); let httpGetCallback; mock('sendHttpGet', (url, cb, opts) => { httpGetCallback = cb; cb(200, { 'Content-Type': 'application/javascript' }, 'body'); }); runCode(mockData); assertApi('claimRequest').wasCalled(); assertApi('sendHttpGet').wasCalledWith( 'https://cdn.jsdelivr.net/gh/snowplow/sp-js-assets@2.18.1/sp.js', httpGetCallback, { timeout: 5000 } ); assertApi('setResponseHeader').wasCalledWith( 'Content-Type', 'application/javascript' ); assertApi('setResponseBody').wasCalledWith('body'); assertApi('getRequestHeader').wasCalledWith('origin'); assertApi('setResponseHeader').wasCalledWith( 'Access-Control-Allow-Origin', 'origin' ); assertApi('setResponseHeader').wasCalledWith( 'Access-Control-Allow-Credentials', 'true' ); assertApi('returnResponse').wasCalled(); - name: Container run with tp2 page view code: | const mockData = { ipInclude: true, populateGaProps: true, serveSpJs: true, customSpJsName: 'example.js', customPostPath: 'custom/path', claimGetRequests: true, includeOriginalTp2Event: true, includeOriginalSelfDescribingEvent: false, includeOriginalContextsArray: false, defaultUserId: true, defaultClientId: true, mergeEntities: false, mergeSelfDesc: false, }; const testEvent = page_view_tp2; // mocks mock('getRequestPath', '/com.snowplowanalytics.snowplow/tp2'); mock('getRequestMethod', 'POST'); mock('getRequestBody', json.stringify(testEvent)); let runContainerCallback; let resultingCommonEvent; mock('runContainer', (e, cb) => { resultingCommonEvent = e; runContainerCallback = cb; cb(); }); runCode(mockData); assertApi('claimRequest').wasCalled(); assertApi('setResponseStatus').wasCalledWith(200); assertApi('setResponseBody').wasCalledWith('ok'); assertApi('getRequestHeader').wasCalledWith('user-agent'); assertApi('getRequestHeader').wasCalledWith('host'); assertApi('getRequestHeader').wasCalledWith('referer'); assertApi('getRequestHeader').wasCalledWith('SP-Anonymous'); assertApi('getRequestHeader').wasCalledWith('origin'); assertApi('setResponseHeader').wasCalledWith( 'Access-Control-Allow-Origin', 'origin' ); assertApi('setResponseHeader').wasCalledWith( 'Access-Control-Allow-Credentials', 'true' ); assertApi('setResponseHeader').wasCalledWith( 'Access-Control-Allow-Headers', 'Content-Type, SP-Anonymous' ); assertApi('setResponseHeader').wasCalledWith( 'Access-Control-Allow-Methods', 'POST, GET, OPTIONS' ); assertApi('returnResponse').wasCalled(); const expectedCommonEvent = { event_name: 'page_view', client_id: 'd54a1904-7798-401a-be0b-1a83bea73634', language: 'en-GB', page_encoding: 'UTF-8', page_hostname: 'snowplowanalytics.com', page_location: 'https://snowplowanalytics.com/', page_path: '/', page_referrer: 'referer', page_title: 'Collect, manage and operationalize behavioral data at scale | Snowplow', screen_resolution: '1920x1080', viewport_size: '745x1302', user_agent: 'user-agent', origin: 'origin', host: 'host', 'x-sp-app_id': 'website', 'x-sp-platform': 'web', 'x-sp-dvce_created_tstamp': '1628586512246', 'x-sp-event_id': '8676de79-0eba-4435-ad95-8a41a8a0129c', 'x-sp-name_tracker': 'sp', 'x-sp-v_tracker': 'js-2.18.1', 'x-sp-domain_sessionid': 'e7580b71-227b-4868-9ea9-322a263ce885', 'x-sp-domain_sessionidx': 1, 'x-sp-domain_userid': 'd54a1904-7798-401a-be0b-1a83bea73634', 'x-sp-br_cookies': '1', 'x-sp-br_colordepth': '24', 'x-sp-br_viewwidth': 745, 'x-sp-br_viewheight': 1302, 'x-sp-dvce_screenwidth': 1920, 'x-sp-dvce_screenheight': 1080, 'x-sp-doc_charset': 'UTF-8', 'x-sp-doc_width': 730, 'x-sp-doc_height': 12393, 'x-sp-dvce_sent_tstamp': '1628586512248', 'x-sp-tp2': { e: 'pv', url: 'https://snowplowanalytics.com/', page: 'Collect, manage and operationalize behavioral data at scale | Snowplow', tv: 'js-2.18.1', tna: 'sp', aid: 'website', p: 'web', tz: 'Europe/London', lang: 'en-GB', cs: 'UTF-8', res: '1920x1080', cd: '24', cookie: '1', eid: '8676de79-0eba-4435-ad95-8a41a8a0129c', dtm: '1628586512246', cx: 'eyJzY2hlbWEiOiJpZ2x1OmNvbS5zbm93cGxvd2FuYWx5dGljcy5zbm93cGxvdy9jb250ZXh0cy9qc29uc2NoZW1hLzEtMC0wIiwiZGF0YSI6W3sic2NoZW1hIjoiaWdsdTpjb20uc25vd3Bsb3dhbmFseXRpY3Muc25vd3Bsb3cvd2ViX3BhZ2UvanNvbnNjaGVtYS8xLTAtMCIsImRhdGEiOnsiaWQiOiJhODZjNDJlNS1iODMxLTQ1YzgtYjcwNi1lMjE0YzI2YjRiM2QifX0seyJzY2hlbWEiOiJpZ2x1Om9yZy53My9QZXJmb3JtYW5jZVRpbWluZy9qc29uc2NoZW1hLzEtMC0wIiwiZGF0YSI6eyJuYXZpZ2F0aW9uU3RhcnQiOjE2Mjg1ODY1MDg2MTAsInVubG9hZEV2ZW50U3RhcnQiOjAsInVubG9hZEV2ZW50RW5kIjowLCJyZWRpcmVjdFN0YXJ0IjowLCJyZWRpcmVjdEVuZCI6MCwiZmV0Y2hTdGFydCI6MTYyODU4NjUwODYxMCwiZG9tYWluTG9va3VwU3RhcnQiOjE2Mjg1ODY1MDg2MzcsImRvbWFpbkxvb2t1cEVuZCI6MTYyODU4NjUwODY5MSwiY29ubmVjdFN0YXJ0IjoxNjI4NTg2NTA4NjkxLCJjb25uZWN0RW5kIjoxNjI4NTg2NTA4NzYzLCJzZWN1cmVDb25uZWN0aW9uU3RhcnQiOjE2Mjg1ODY1MDg3MjEsInJlcXVlc3RTdGFydCI6MTYyODU4NjUwODc2MywicmVzcG9uc2VTdGFydCI6MTYyODU4NjUwODc5NywicmVzcG9uc2VFbmQiOjE2Mjg1ODY1MDg4MjEsImRvbUxvYWRpbmciOjE2Mjg1ODY1MDkwNzYsImRvbUludGVyYWN0aXZlIjoxNjI4NTg2NTA5MzgxLCJkb21Db250ZW50TG9hZGVkRXZlbnRTdGFydCI6MTYyODU4NjUwOTQwOCwiZG9tQ29udGVudExvYWRlZEV2ZW50RW5kIjoxNjI4NTg2NTA5NDE3LCJkb21Db21wbGV0ZSI6MTYyODU4NjUxMDMzMiwibG9hZEV2ZW50U3RhcnQiOjE2Mjg1ODY1MTAzMzIsImxvYWRFdmVudEVuZCI6MTYyODU4NjUxMDMzNH19XX0', vp: '745x1302', ds: '730x12393', vid: '1', sid: 'e7580b71-227b-4868-9ea9-322a263ce885', duid: 'd54a1904-7798-401a-be0b-1a83bea73634', stm: '1628586512248', }, 'x-sp-contexts_com_snowplowanalytics_snowplow_web_page_1': [ { id: 'a86c42e5-b831-45c8-b706-e214c26b4b3d' }, ], 'x-sp-contexts_org_w3_performance_timing_1': [ { navigationStart: 1628586508610, unloadEventStart: 0, unloadEventEnd: 0, redirectStart: 0, redirectEnd: 0, fetchStart: 1628586508610, domainLookupStart: 1628586508637, domainLookupEnd: 1628586508691, connectStart: 1628586508691, connectEnd: 1628586508763, secureConnectionStart: 1628586508721, requestStart: 1628586508763, responseStart: 1628586508797, responseEnd: 1628586508821, domLoading: 1628586509076, domInteractive: 1628586509381, domContentLoadedEventStart: 1628586509408, domContentLoadedEventEnd: 1628586509417, domComplete: 1628586510332, loadEventStart: 1628586510332, loadEventEnd: 1628586510334, }, ], ga_session_id: 'e7580b71-227b-4868-9ea9-322a263ce885', ga_session_number: '1', 'x-ga-mp2-seg': '1', 'x-ga-protocol_version': '2', 'x-ga-page_id': 'a86c42e5-b831-45c8-b706-e214c26b4b3d', ip_override: '1.2.3.4', }; assertThat(resultingCommonEvent).isEqualTo(expectedCommonEvent); assertApi('runContainer').wasCalledWith( expectedCommonEvent, runContainerCallback ); - name: Container run with tp2 page view - with identified user code: | const funA = (x, ev) => x[0]; const mockData = { ipInclude: true, populateGaProps: true, serveSpJs: true, customSpJsName: 'example.js', customPostPath: 'custom/path', claimGetRequests: true, includeOriginalTp2Event: true, includeOriginalSelfDescribingEvent: false, includeOriginalContextsArray: false, defaultUserId: true, defaultClientId: true, mergeEntities: true, entityMergeRules: [ { schema: 'iglu:com.snowplowanalytics.snowplow/web_page/jsonschema/1-0-0', versionPolicy: 'control', prefix: 'x-test-', mergeLevel: 'rootLevel', customPath: '', keepOriginal: 'keep', customTransformFun: funA, }, { schema: 'x-sp-contexts_org_w3_performance_timing_1', versionPolicy: 'control', prefix: 'x-test-perf_timing_', mergeLevel: 'customPath', customPath: 'foobar', keepOriginal: 'discard', customTransformFun: '', }, ], mergeSelfDesc: false, }; const testEvent = json.parse(json.stringify(page_view_tp2)); testEvent.data[0].uid = 'snow123'; // mocks mock('getRequestPath', '/com.snowplowanalytics.snowplow/tp2'); mock('getRequestMethod', 'POST'); mock('getRequestBody', json.stringify(testEvent)); let runContainerCallback; let resultingCommonEvent; mock('runContainer', (e, cb) => { resultingCommonEvent = e; runContainerCallback = cb; cb(); }); runCode(mockData); assertApi('claimRequest').wasCalled(); assertApi('setResponseStatus').wasCalledWith(200); assertApi('setResponseBody').wasCalledWith('ok'); assertApi('getRequestHeader').wasCalledWith('user-agent'); assertApi('getRequestHeader').wasCalledWith('host'); assertApi('getRequestHeader').wasCalledWith('referer'); assertApi('getRequestHeader').wasCalledWith('SP-Anonymous'); assertApi('getRequestHeader').wasCalledWith('origin'); assertApi('setResponseHeader').wasCalledWith( 'Access-Control-Allow-Origin', 'origin' ); assertApi('setResponseHeader').wasCalledWith( 'Access-Control-Allow-Credentials', 'true' ); assertApi('setResponseHeader').wasCalledWith( 'Access-Control-Allow-Headers', 'Content-Type, SP-Anonymous' ); assertApi('setResponseHeader').wasCalledWith( 'Access-Control-Allow-Methods', 'POST, GET, OPTIONS' ); assertApi('returnResponse').wasCalled(); const expectedCommonEvent = { event_name: 'page_view', client_id: 'd54a1904-7798-401a-be0b-1a83bea73634', language: 'en-GB', page_encoding: 'UTF-8', page_hostname: 'snowplowanalytics.com', page_location: 'https://snowplowanalytics.com/', page_path: '/', page_referrer: 'referer', page_title: 'Collect, manage and operationalize behavioral data at scale | Snowplow', screen_resolution: '1920x1080', user_id: 'snow123', viewport_size: '745x1302', user_agent: 'user-agent', origin: 'origin', host: 'host', 'x-sp-app_id': 'website', 'x-sp-platform': 'web', 'x-sp-dvce_created_tstamp': '1628586512246', 'x-sp-event_id': '8676de79-0eba-4435-ad95-8a41a8a0129c', 'x-sp-name_tracker': 'sp', 'x-sp-v_tracker': 'js-2.18.1', 'x-sp-domain_sessionid': 'e7580b71-227b-4868-9ea9-322a263ce885', 'x-sp-domain_sessionidx': 1, 'x-sp-domain_userid': 'd54a1904-7798-401a-be0b-1a83bea73634', 'x-sp-user_id': 'snow123', 'x-sp-br_cookies': '1', 'x-sp-br_colordepth': '24', 'x-sp-br_viewwidth': 745, 'x-sp-br_viewheight': 1302, 'x-sp-dvce_screenwidth': 1920, 'x-sp-dvce_screenheight': 1080, 'x-sp-doc_charset': 'UTF-8', 'x-sp-doc_width': 730, 'x-sp-doc_height': 12393, 'x-sp-dvce_sent_tstamp': '1628586512248', 'x-sp-tp2': { e: 'pv', url: 'https://snowplowanalytics.com/', page: 'Collect, manage and operationalize behavioral data at scale | Snowplow', tv: 'js-2.18.1', tna: 'sp', aid: 'website', p: 'web', tz: 'Europe/London', lang: 'en-GB', cs: 'UTF-8', res: '1920x1080', cd: '24', cookie: '1', eid: '8676de79-0eba-4435-ad95-8a41a8a0129c', dtm: '1628586512246', cx: 'eyJzY2hlbWEiOiJpZ2x1OmNvbS5zbm93cGxvd2FuYWx5dGljcy5zbm93cGxvdy9jb250ZXh0cy9qc29uc2NoZW1hLzEtMC0wIiwiZGF0YSI6W3sic2NoZW1hIjoiaWdsdTpjb20uc25vd3Bsb3dhbmFseXRpY3Muc25vd3Bsb3cvd2ViX3BhZ2UvanNvbnNjaGVtYS8xLTAtMCIsImRhdGEiOnsiaWQiOiJhODZjNDJlNS1iODMxLTQ1YzgtYjcwNi1lMjE0YzI2YjRiM2QifX0seyJzY2hlbWEiOiJpZ2x1Om9yZy53My9QZXJmb3JtYW5jZVRpbWluZy9qc29uc2NoZW1hLzEtMC0wIiwiZGF0YSI6eyJuYXZpZ2F0aW9uU3RhcnQiOjE2Mjg1ODY1MDg2MTAsInVubG9hZEV2ZW50U3RhcnQiOjAsInVubG9hZEV2ZW50RW5kIjowLCJyZWRpcmVjdFN0YXJ0IjowLCJyZWRpcmVjdEVuZCI6MCwiZmV0Y2hTdGFydCI6MTYyODU4NjUwODYxMCwiZG9tYWluTG9va3VwU3RhcnQiOjE2Mjg1ODY1MDg2MzcsImRvbWFpbkxvb2t1cEVuZCI6MTYyODU4NjUwODY5MSwiY29ubmVjdFN0YXJ0IjoxNjI4NTg2NTA4NjkxLCJjb25uZWN0RW5kIjoxNjI4NTg2NTA4NzYzLCJzZWN1cmVDb25uZWN0aW9uU3RhcnQiOjE2Mjg1ODY1MDg3MjEsInJlcXVlc3RTdGFydCI6MTYyODU4NjUwODc2MywicmVzcG9uc2VTdGFydCI6MTYyODU4NjUwODc5NywicmVzcG9uc2VFbmQiOjE2Mjg1ODY1MDg4MjEsImRvbUxvYWRpbmciOjE2Mjg1ODY1MDkwNzYsImRvbUludGVyYWN0aXZlIjoxNjI4NTg2NTA5MzgxLCJkb21Db250ZW50TG9hZGVkRXZlbnRTdGFydCI6MTYyODU4NjUwOTQwOCwiZG9tQ29udGVudExvYWRlZEV2ZW50RW5kIjoxNjI4NTg2NTA5NDE3LCJkb21Db21wbGV0ZSI6MTYyODU4NjUxMDMzMiwibG9hZEV2ZW50U3RhcnQiOjE2Mjg1ODY1MTAzMzIsImxvYWRFdmVudEVuZCI6MTYyODU4NjUxMDMzNH19XX0', vp: '745x1302', ds: '730x12393', vid: '1', sid: 'e7580b71-227b-4868-9ea9-322a263ce885', duid: 'd54a1904-7798-401a-be0b-1a83bea73634', stm: '1628586512248', uid: 'snow123', }, 'x-test-id': 'a86c42e5-b831-45c8-b706-e214c26b4b3d', 'x-sp-contexts_com_snowplowanalytics_snowplow_web_page_1': [ { id: 'a86c42e5-b831-45c8-b706-e214c26b4b3d' }, ], foobar: { 'x-test-perf_timing_navigationStart': 1628586508610, 'x-test-perf_timing_unloadEventStart': 0, 'x-test-perf_timing_unloadEventEnd': 0, 'x-test-perf_timing_redirectStart': 0, 'x-test-perf_timing_redirectEnd': 0, 'x-test-perf_timing_fetchStart': 1628586508610, 'x-test-perf_timing_domainLookupStart': 1628586508637, 'x-test-perf_timing_domainLookupEnd': 1628586508691, 'x-test-perf_timing_connectStart': 1628586508691, 'x-test-perf_timing_connectEnd': 1628586508763, 'x-test-perf_timing_secureConnectionStart': 1628586508721, 'x-test-perf_timing_requestStart': 1628586508763, 'x-test-perf_timing_responseStart': 1628586508797, 'x-test-perf_timing_responseEnd': 1628586508821, 'x-test-perf_timing_domLoading': 1628586509076, 'x-test-perf_timing_domInteractive': 1628586509381, 'x-test-perf_timing_domContentLoadedEventStart': 1628586509408, 'x-test-perf_timing_domContentLoadedEventEnd': 1628586509417, 'x-test-perf_timing_domComplete': 1628586510332, 'x-test-perf_timing_loadEventStart': 1628586510332, 'x-test-perf_timing_loadEventEnd': 1628586510334, }, ga_session_id: 'e7580b71-227b-4868-9ea9-322a263ce885', ga_session_number: '1', 'x-ga-mp2-seg': '1', 'x-ga-protocol_version': '2', 'x-ga-page_id': 'a86c42e5-b831-45c8-b706-e214c26b4b3d', ip_override: '1.2.3.4', }; assertThat(resultingCommonEvent).isEqualTo(expectedCommonEvent); assertApi('runContainer').wasCalledWith( expectedCommonEvent, runContainerCallback ); - name: Container run with tp2 page-view and SP-Anonymous enabled code: | const mockData = { ipInclude: true, populateGaProps: true, serveSpJs: true, customSpJsName: 'example.js', customPostPath: 'custom/path', claimGetRequests: true, includeOriginalTp2Event: true, includeOriginalSelfDescribingEvent: false, includeOriginalContextsArray: false, defaultUserId: true, defaultClientId: true, mergeEntities: false, mergeSelfDesc: false, }; const testEvent = json.parse(json.stringify(page_view_tp2)); testEvent.data[0].uid = 'snow123'; // mocks mock('getRequestPath', '/com.snowplowanalytics.snowplow/tp2'); mock('getRequestMethod', 'POST'); mock('getRequestBody', json.stringify(testEvent)); mock('getRequestHeader', (header) => { if (header === 'SP-Anonymous') { return '*'; } return header; }); let runContainerCallback; let resultingCommonEvent; mock('runContainer', (e, cb) => { resultingCommonEvent = e; runContainerCallback = cb; cb(); }); runCode(mockData); assertApi('claimRequest').wasCalled(); assertApi('setResponseStatus').wasCalledWith(200); assertApi('setResponseBody').wasCalledWith('ok'); assertApi('getRequestHeader').wasCalledWith('user-agent'); assertApi('getRequestHeader').wasCalledWith('host'); assertApi('getRequestHeader').wasCalledWith('referer'); assertApi('getRequestHeader').wasCalledWith('SP-Anonymous'); assertApi('getRequestHeader').wasCalledWith('origin'); assertApi('setResponseHeader').wasCalledWith( 'Access-Control-Allow-Origin', 'origin' ); assertApi('setResponseHeader').wasCalledWith( 'Access-Control-Allow-Credentials', 'true' ); assertApi('setResponseHeader').wasCalledWith( 'Access-Control-Allow-Headers', 'Content-Type, SP-Anonymous' ); assertApi('setResponseHeader').wasCalledWith( 'Access-Control-Allow-Methods', 'POST, GET, OPTIONS' ); assertApi('returnResponse').wasCalled(); const expectedCommonEvent = { event_name: 'page_view', client_id: 'd54a1904-7798-401a-be0b-1a83bea73634', language: 'en-GB', page_encoding: 'UTF-8', page_hostname: 'snowplowanalytics.com', page_location: 'https://snowplowanalytics.com/', page_path: '/', page_referrer: 'referer', page_title: 'Collect, manage and operationalize behavioral data at scale | Snowplow', screen_resolution: '1920x1080', user_id: 'snow123', viewport_size: '745x1302', user_agent: 'user-agent', origin: 'origin', host: 'host', 'x-sp-anonymous': '*', 'x-sp-app_id': 'website', 'x-sp-platform': 'web', 'x-sp-dvce_created_tstamp': '1628586512246', 'x-sp-event_id': '8676de79-0eba-4435-ad95-8a41a8a0129c', 'x-sp-name_tracker': 'sp', 'x-sp-v_tracker': 'js-2.18.1', 'x-sp-domain_sessionid': 'e7580b71-227b-4868-9ea9-322a263ce885', 'x-sp-domain_sessionidx': 1, 'x-sp-domain_userid': 'd54a1904-7798-401a-be0b-1a83bea73634', 'x-sp-user_id': 'snow123', 'x-sp-br_cookies': '1', 'x-sp-br_colordepth': '24', 'x-sp-br_viewwidth': 745, 'x-sp-br_viewheight': 1302, 'x-sp-dvce_screenwidth': 1920, 'x-sp-dvce_screenheight': 1080, 'x-sp-doc_charset': 'UTF-8', 'x-sp-doc_width': 730, 'x-sp-doc_height': 12393, 'x-sp-dvce_sent_tstamp': '1628586512248', 'x-sp-tp2': { e: 'pv', url: 'https://snowplowanalytics.com/', page: 'Collect, manage and operationalize behavioral data at scale | Snowplow', tv: 'js-2.18.1', tna: 'sp', aid: 'website', p: 'web', tz: 'Europe/London', lang: 'en-GB', cs: 'UTF-8', res: '1920x1080', cd: '24', cookie: '1', eid: '8676de79-0eba-4435-ad95-8a41a8a0129c', dtm: '1628586512246', cx: 'eyJzY2hlbWEiOiJpZ2x1OmNvbS5zbm93cGxvd2FuYWx5dGljcy5zbm93cGxvdy9jb250ZXh0cy9qc29uc2NoZW1hLzEtMC0wIiwiZGF0YSI6W3sic2NoZW1hIjoiaWdsdTpjb20uc25vd3Bsb3dhbmFseXRpY3Muc25vd3Bsb3cvd2ViX3BhZ2UvanNvbnNjaGVtYS8xLTAtMCIsImRhdGEiOnsiaWQiOiJhODZjNDJlNS1iODMxLTQ1YzgtYjcwNi1lMjE0YzI2YjRiM2QifX0seyJzY2hlbWEiOiJpZ2x1Om9yZy53My9QZXJmb3JtYW5jZVRpbWluZy9qc29uc2NoZW1hLzEtMC0wIiwiZGF0YSI6eyJuYXZpZ2F0aW9uU3RhcnQiOjE2Mjg1ODY1MDg2MTAsInVubG9hZEV2ZW50U3RhcnQiOjAsInVubG9hZEV2ZW50RW5kIjowLCJyZWRpcmVjdFN0YXJ0IjowLCJyZWRpcmVjdEVuZCI6MCwiZmV0Y2hTdGFydCI6MTYyODU4NjUwODYxMCwiZG9tYWluTG9va3VwU3RhcnQiOjE2Mjg1ODY1MDg2MzcsImRvbWFpbkxvb2t1cEVuZCI6MTYyODU4NjUwODY5MSwiY29ubmVjdFN0YXJ0IjoxNjI4NTg2NTA4NjkxLCJjb25uZWN0RW5kIjoxNjI4NTg2NTA4NzYzLCJzZWN1cmVDb25uZWN0aW9uU3RhcnQiOjE2Mjg1ODY1MDg3MjEsInJlcXVlc3RTdGFydCI6MTYyODU4NjUwODc2MywicmVzcG9uc2VTdGFydCI6MTYyODU4NjUwODc5NywicmVzcG9uc2VFbmQiOjE2Mjg1ODY1MDg4MjEsImRvbUxvYWRpbmciOjE2Mjg1ODY1MDkwNzYsImRvbUludGVyYWN0aXZlIjoxNjI4NTg2NTA5MzgxLCJkb21Db250ZW50TG9hZGVkRXZlbnRTdGFydCI6MTYyODU4NjUwOTQwOCwiZG9tQ29udGVudExvYWRlZEV2ZW50RW5kIjoxNjI4NTg2NTA5NDE3LCJkb21Db21wbGV0ZSI6MTYyODU4NjUxMDMzMiwibG9hZEV2ZW50U3RhcnQiOjE2Mjg1ODY1MTAzMzIsImxvYWRFdmVudEVuZCI6MTYyODU4NjUxMDMzNH19XX0', vp: '745x1302', ds: '730x12393', vid: '1', sid: 'e7580b71-227b-4868-9ea9-322a263ce885', duid: 'd54a1904-7798-401a-be0b-1a83bea73634', stm: '1628586512248', uid: 'snow123', }, 'x-sp-contexts_com_snowplowanalytics_snowplow_web_page_1': [ { id: 'a86c42e5-b831-45c8-b706-e214c26b4b3d' }, ], 'x-sp-contexts_org_w3_performance_timing_1': [ { navigationStart: 1628586508610, unloadEventStart: 0, unloadEventEnd: 0, redirectStart: 0, redirectEnd: 0, fetchStart: 1628586508610, domainLookupStart: 1628586508637, domainLookupEnd: 1628586508691, connectStart: 1628586508691, connectEnd: 1628586508763, secureConnectionStart: 1628586508721, requestStart: 1628586508763, responseStart: 1628586508797, responseEnd: 1628586508821, domLoading: 1628586509076, domInteractive: 1628586509381, domContentLoadedEventStart: 1628586509408, domContentLoadedEventEnd: 1628586509417, domComplete: 1628586510332, loadEventStart: 1628586510332, loadEventEnd: 1628586510334, }, ], ga_session_id: 'e7580b71-227b-4868-9ea9-322a263ce885', ga_session_number: '1', 'x-ga-mp2-seg': '1', 'x-ga-protocol_version': '2', 'x-ga-page_id': 'a86c42e5-b831-45c8-b706-e214c26b4b3d', }; assertThat(resultingCommonEvent).isEqualTo(expectedCommonEvent); assertApi('runContainer').wasCalledWith( expectedCommonEvent, runContainerCallback ); - name: Container run with /i GET page view code: | const mockData = { ipInclude: true, populateGaProps: true, serveSpJs: true, customSpJsName: 'example.js', customPostPath: 'custom/path', claimGetRequests: true, includeOriginalTp2Event: true, includeOriginalSelfDescribingEvent: false, includeOriginalContextsArray: false, defaultUserId: true, defaultClientId: true, mergeEntities: false, mergeSelfDesc: false, }; const testEvent = page_view_tp2; // mocks mock('getRequestPath', '/i'); mock('getRequestQueryParameters', testEvent.data[0]); mock('getRequestMethod', 'GET'); let runContainerCallback; let resultingCommonEvent; mock('runContainer', (e, cb) => { resultingCommonEvent = e; runContainerCallback = cb; cb(); }); runCode(mockData); assertApi('claimRequest').wasCalled(); assertApi('setResponseStatus').wasCalledWith(200); assertApi('setResponseBody').wasNotCalled(); assertApi('setPixelResponse').wasCalled(); assertApi('getRequestHeader').wasCalledWith('user-agent'); assertApi('getRequestHeader').wasCalledWith('host'); assertApi('getRequestHeader').wasCalledWith('referer'); assertApi('getRequestHeader').wasCalledWith('SP-Anonymous'); assertApi('getRequestHeader').wasCalledWith('origin'); assertApi('setResponseHeader').wasCalledWith( 'Access-Control-Allow-Origin', 'origin' ); assertApi('setResponseHeader').wasCalledWith( 'Access-Control-Allow-Credentials', 'true' ); assertApi('setResponseHeader').wasCalledWith( 'Access-Control-Allow-Headers', 'Content-Type, SP-Anonymous' ); assertApi('setResponseHeader').wasCalledWith( 'Access-Control-Allow-Methods', 'POST, GET, OPTIONS' ); assertApi('returnResponse').wasCalled(); const expectedCommonEvent = { event_name: 'page_view', client_id: 'd54a1904-7798-401a-be0b-1a83bea73634', language: 'en-GB', page_encoding: 'UTF-8', page_hostname: 'snowplowanalytics.com', page_location: 'https://snowplowanalytics.com/', page_path: '/', page_referrer: 'referer', page_title: 'Collect, manage and operationalize behavioral data at scale | Snowplow', screen_resolution: '1920x1080', viewport_size: '745x1302', user_agent: 'user-agent', origin: 'origin', host: 'host', 'x-sp-app_id': 'website', 'x-sp-platform': 'web', 'x-sp-dvce_created_tstamp': '1628586512246', 'x-sp-event_id': '8676de79-0eba-4435-ad95-8a41a8a0129c', 'x-sp-name_tracker': 'sp', 'x-sp-v_tracker': 'js-2.18.1', 'x-sp-domain_sessionid': 'e7580b71-227b-4868-9ea9-322a263ce885', 'x-sp-domain_sessionidx': 1, 'x-sp-domain_userid': 'd54a1904-7798-401a-be0b-1a83bea73634', 'x-sp-br_cookies': '1', 'x-sp-br_colordepth': '24', 'x-sp-br_viewwidth': 745, 'x-sp-br_viewheight': 1302, 'x-sp-dvce_screenwidth': 1920, 'x-sp-dvce_screenheight': 1080, 'x-sp-doc_charset': 'UTF-8', 'x-sp-doc_width': 730, 'x-sp-doc_height': 12393, 'x-sp-dvce_sent_tstamp': '1628586512248', 'x-sp-tp2': { e: 'pv', url: 'https://snowplowanalytics.com/', page: 'Collect, manage and operationalize behavioral data at scale | Snowplow', tv: 'js-2.18.1', tna: 'sp', aid: 'website', p: 'web', tz: 'Europe/London', lang: 'en-GB', cs: 'UTF-8', res: '1920x1080', cd: '24', cookie: '1', eid: '8676de79-0eba-4435-ad95-8a41a8a0129c', dtm: '1628586512246', cx: 'eyJzY2hlbWEiOiJpZ2x1OmNvbS5zbm93cGxvd2FuYWx5dGljcy5zbm93cGxvdy9jb250ZXh0cy9qc29uc2NoZW1hLzEtMC0wIiwiZGF0YSI6W3sic2NoZW1hIjoiaWdsdTpjb20uc25vd3Bsb3dhbmFseXRpY3Muc25vd3Bsb3cvd2ViX3BhZ2UvanNvbnNjaGVtYS8xLTAtMCIsImRhdGEiOnsiaWQiOiJhODZjNDJlNS1iODMxLTQ1YzgtYjcwNi1lMjE0YzI2YjRiM2QifX0seyJzY2hlbWEiOiJpZ2x1Om9yZy53My9QZXJmb3JtYW5jZVRpbWluZy9qc29uc2NoZW1hLzEtMC0wIiwiZGF0YSI6eyJuYXZpZ2F0aW9uU3RhcnQiOjE2Mjg1ODY1MDg2MTAsInVubG9hZEV2ZW50U3RhcnQiOjAsInVubG9hZEV2ZW50RW5kIjowLCJyZWRpcmVjdFN0YXJ0IjowLCJyZWRpcmVjdEVuZCI6MCwiZmV0Y2hTdGFydCI6MTYyODU4NjUwODYxMCwiZG9tYWluTG9va3VwU3RhcnQiOjE2Mjg1ODY1MDg2MzcsImRvbWFpbkxvb2t1cEVuZCI6MTYyODU4NjUwODY5MSwiY29ubmVjdFN0YXJ0IjoxNjI4NTg2NTA4NjkxLCJjb25uZWN0RW5kIjoxNjI4NTg2NTA4NzYzLCJzZWN1cmVDb25uZWN0aW9uU3RhcnQiOjE2Mjg1ODY1MDg3MjEsInJlcXVlc3RTdGFydCI6MTYyODU4NjUwODc2MywicmVzcG9uc2VTdGFydCI6MTYyODU4NjUwODc5NywicmVzcG9uc2VFbmQiOjE2Mjg1ODY1MDg4MjEsImRvbUxvYWRpbmciOjE2Mjg1ODY1MDkwNzYsImRvbUludGVyYWN0aXZlIjoxNjI4NTg2NTA5MzgxLCJkb21Db250ZW50TG9hZGVkRXZlbnRTdGFydCI6MTYyODU4NjUwOTQwOCwiZG9tQ29udGVudExvYWRlZEV2ZW50RW5kIjoxNjI4NTg2NTA5NDE3LCJkb21Db21wbGV0ZSI6MTYyODU4NjUxMDMzMiwibG9hZEV2ZW50U3RhcnQiOjE2Mjg1ODY1MTAzMzIsImxvYWRFdmVudEVuZCI6MTYyODU4NjUxMDMzNH19XX0', vp: '745x1302', ds: '730x12393', vid: '1', sid: 'e7580b71-227b-4868-9ea9-322a263ce885', duid: 'd54a1904-7798-401a-be0b-1a83bea73634', stm: '1628586512248', }, 'x-sp-contexts_com_snowplowanalytics_snowplow_web_page_1': [ { id: 'a86c42e5-b831-45c8-b706-e214c26b4b3d' }, ], 'x-sp-contexts_org_w3_performance_timing_1': [ { navigationStart: 1628586508610, unloadEventStart: 0, unloadEventEnd: 0, redirectStart: 0, redirectEnd: 0, fetchStart: 1628586508610, domainLookupStart: 1628586508637, domainLookupEnd: 1628586508691, connectStart: 1628586508691, connectEnd: 1628586508763, secureConnectionStart: 1628586508721, requestStart: 1628586508763, responseStart: 1628586508797, responseEnd: 1628586508821, domLoading: 1628586509076, domInteractive: 1628586509381, domContentLoadedEventStart: 1628586509408, domContentLoadedEventEnd: 1628586509417, domComplete: 1628586510332, loadEventStart: 1628586510332, loadEventEnd: 1628586510334, }, ], ga_session_id: 'e7580b71-227b-4868-9ea9-322a263ce885', ga_session_number: '1', 'x-ga-mp2-seg': '1', 'x-ga-protocol_version': '2', 'x-ga-page_id': 'a86c42e5-b831-45c8-b706-e214c26b4b3d', ip_override: '1.2.3.4', }; assertThat(resultingCommonEvent).isEqualTo(expectedCommonEvent); assertApi('runContainer').wasCalledWith( expectedCommonEvent, runContainerCallback ); - name: Container run with user_data context code: | const mockData = { ipInclude: true, populateGaProps: true, serveSpJs: true, customSpJsName: 'example.js', customPostPath: 'custom/path', claimGetRequests: true, includeOriginalTp2Event: false, includeOriginalSelfDescribingEvent: false, includeOriginalContextsArray: false, defaultUserId: true, defaultClientId: true, mergeEntities: true, entityMergeRules: [ { schema: 'iglu:org.whatwg/media_element/jsonschema/1-0-0', versionPolicy: 'free', prefix: '', mergeLevel: 'rootLevel', customPath: '', keepOriginal: 'discard', customTransformFun: '', }, { schema: 'contexts_com_snowplowanalytics_snowplow_media_player', versionPolicy: 'free', prefix: 'x-test-mp_', mergeLevel: 'customPath', customPath: 'user_data', keepOriginal: 'discard', customTransformFun: '', }, { schema: 'iglu:com.snowplowanalytics.snowplow/mobile_context/jsonschema/1-0-2', versionPolicy: 'control', prefix: '', mergeLevel: 'rootLevel', customPath: '', keepOriginal: 'discard', customTransformFun: '', }, { schema: 'x-sp-contexts_com_snowplowanalytics_snowplow_client_session', versionPolicy: 'free', prefix: '', mergeLevel: 'customPath', customPath: 'user_data.client_session.0', keepOriginal: 'keep', customTransformFun: '', }, ], mergeSelfDesc: true, selfDescMergeRules: [ { schema: 'iglu:com.snowplowanalytics.snowplow/media_player_event/jsonschema/1-0-0', versionPolicy: 'control', prefix: 'media_event_', mergeLevel: 'rootLevel', customPath: '', keepOriginal: 'keep', customTransformFun: '' }, ], }; const testEvent = mediaEventTp2; // mocks mock('getRequestPath', '/com.snowplowanalytics.snowplow/tp2'); mock('getRequestMethod', 'POST'); mock('getRequestBody', json.stringify(testEvent)); mock('getRequestHeader', (header) => { if (header === 'SP-Anonymous') { return '*'; } return header; }); let runContainerCallback; let resultingCommonEvent; mock('runContainer', (e, cb) => { resultingCommonEvent = e; runContainerCallback = cb; cb(); }); runCode(mockData); assertApi('claimRequest').wasCalled(); assertApi('setResponseStatus').wasCalledWith(200); assertApi('setResponseBody').wasCalledWith('ok'); assertApi('getRequestHeader').wasCalledWith('user-agent'); assertApi('getRequestHeader').wasCalledWith('host'); assertApi('getRequestHeader').wasCalledWith('referer'); assertApi('getRequestHeader').wasCalledWith('SP-Anonymous'); assertApi('getRequestHeader').wasCalledWith('origin'); assertApi('setResponseHeader').wasCalledWith( 'Access-Control-Allow-Origin', 'origin' ); assertApi('setResponseHeader').wasCalledWith( 'Access-Control-Allow-Credentials', 'true' ); assertApi('setResponseHeader').wasCalledWith( 'Access-Control-Allow-Headers', 'Content-Type, SP-Anonymous' ); assertApi('setResponseHeader').wasCalledWith( 'Access-Control-Allow-Methods', 'POST, GET, OPTIONS' ); assertApi('returnResponse').wasCalled(); const expectedCommonEvent = { event_name: 'media_player_event', language: 'en-US', page_encoding: 'windows-1252', page_hostname: 'localhost', page_location: 'http://localhost:8000/', page_path: '/', page_referrer: 'referer', screen_resolution: '1920x1080', viewport_size: '744x971', user_agent: 'user-agent', origin: 'origin', host: 'host', 'x-sp-anonymous': '*', 'x-sp-app_id': 'media-test', 'x-sp-platform': 'web', 'x-sp-dvce_created_tstamp': '1697609091769', 'x-sp-event_id': '011c85b9-0ee1-4f01-a9ca-7bd11db0c811', 'x-sp-name_tracker': 'spTest', 'x-sp-v_tracker': 'js-3.16.0', 'x-sp-domain_sessionid': '6ce287c3-e58d-4501-9586-1062d9b2d80c', 'x-sp-domain_sessionidx': 1, 'x-sp-domain_userid': 'fd97960a-bcb9-4530-8446-e370e1952e5e', 'x-sp-user_id': 'media_tester', 'x-sp-br_cookies': '1', 'x-sp-br_colordepth': '24', 'x-sp-br_viewwidth': 744, 'x-sp-br_viewheight': 971, 'x-sp-dvce_screenwidth': 1920, 'x-sp-dvce_screenheight': 1080, 'x-sp-doc_charset': 'windows-1252', 'x-sp-doc_width': 729, 'x-sp-doc_height': 1211, 'x-sp-dvce_sent_tstamp': '1697609091773', 'x-sp-self_describing_event_com_snowplowanalytics_snowplow_media_player_event_1': { type: 'pause' }, // contexts_org_whatwg_media_element_1 props at root level htmlId: 'bunny-mp4', mediaType: 'VIDEO', autoPlay: false, buffered: [{ start: 0, end: 13.424 }], controls: true, currentSrc: 'https://archive.org/download/BigBuckBunny_124/Content/big_buck_bunny_720p_surround.mp4', defaultMuted: false, defaultPlaybackRate: 1, // error: null, // gets cleaned-up by cleanObject networkState: 'NETWORK_LOADING', preload: 'metadata', readyState: 'HAVE_ENOUGH_DATA', seekable: [{ start: 0, end: 596.48 }], seeking: false, src: 'https://archive.org/download/BigBuckBunny_124/Content/big_buck_bunny_720p_surround.mp4', textTracks: [], fileExtension: 'mp4', fullscreen: false, pictureInPicture: false, 'x-sp-contexts_org_whatwg_video_element_1': [ { poster: '', videoHeight: 360, videoWidth: 640 }, ], 'x-sp-contexts_com_snowplowanalytics_snowplow_web_page_1': [ { id: '586b753d-c961-4852-a164-f641c9a4404f' }, ], 'x-sp-contexts_com_google_tag-manager_server-side_user_data_1': [ { email_address: 'foo@example.com', phone_number: '+15551234567', address: { first_name: 'Jane', last_name: 'Doe', street: '123 Fake St', city: 'San Francisco', region: 'CA', postal_code: '94016', country: 'US', }, }, ], osType: 'testOsType', osVersion: 'testOsVersion', deviceManufacturer: 'testDevMan', deviceModel: 'testDevModel', 'x-sp-contexts_com_snowplowanalytics_snowplow_client_session_1': [ { userId: 'fd97960a-bcb9-4530-8446-e370e1952e5e', sessionId: '6ce287c3-e58d-4501-9586-1062d9b2d80c', eventIndex: 4, sessionIndex: 1, previousSessionId: null, storageMechanism: 'COOKIE_1', firstEventId: 'f8525402-1483-4fc4-8fc7-3eea2559127e', firstEventTimestamp: '2023-10-18T06:04:39.898Z', }, ], user_data: { email_address: 'foo@example.com', phone_number: '+15551234567', address: { first_name: 'Jane', last_name: 'Doe', street: '123 Fake St', city: 'San Francisco', region: 'CA', postal_code: '94016', country: 'US', }, 'x-test-mp_currentTime': 5.801593, 'x-test-mp_duration': 596.48, 'x-test-mp_ended': false, 'x-test-mp_loop': false, 'x-test-mp_muted': false, 'x-test-mp_paused': true, 'x-test-mp_playbackRate': 1, 'x-test-mp_volume': 100, client_session: [ { userId: 'fd97960a-bcb9-4530-8446-e370e1952e5e', sessionId: '6ce287c3-e58d-4501-9586-1062d9b2d80c', eventIndex: 4, sessionIndex: 1, previousSessionId: null, storageMechanism: 'COOKIE_1', firstEventId: 'f8525402-1483-4fc4-8fc7-3eea2559127e', firstEventTimestamp: '2023-10-18T06:04:39.898Z', }, ], }, ga_session_id: '6ce287c3-e58d-4501-9586-1062d9b2d80c', ga_session_number: '1', 'x-ga-mp2-seg': '1', 'x-ga-protocol_version': '2', 'x-ga-page_id': '586b753d-c961-4852-a164-f641c9a4404f', client_id: 'fd97960a-bcb9-4530-8446-e370e1952e5e', user_id: 'media_tester', media_event_type: 'pause', }; assertThat(resultingCommonEvent).isEqualTo(expectedCommonEvent); assertApi('runContainer').wasCalledWith( expectedCommonEvent, runContainerCallback ); - name: Container run with client_session context from mobile tracker code: | const mockData = { ipInclude: true, populateGaProps: true, serveSpJs: true, customSpJsName: 'example.js', customPostPath: 'custom/path', claimGetRequests: true, includeOriginalTp2Event: true, includeOriginalSelfDescribingEvent: false, includeOriginalContextsArray: false, defaultUserId: true, defaultClientId: true, mergeEntities: false, mergeSelfDesc: false, }; const testEvent = page_view_mobile; // mocks mock('getRequestPath', '/com.snowplowanalytics.snowplow/tp2'); mock('getRequestMethod', 'POST'); mock('getRequestBody', json.stringify(testEvent)); let runContainerCallback; let resultingCommonEvent; mock('runContainer', (e, cb) => { resultingCommonEvent = e; runContainerCallback = cb; cb(); }); runCode(mockData); assertApi('claimRequest').wasCalled(); assertApi('setResponseStatus').wasCalledWith(200); assertApi('setResponseBody').wasCalledWith('ok'); assertApi('getRequestHeader').wasCalledWith('user-agent'); assertApi('getRequestHeader').wasCalledWith('host'); assertApi('getRequestHeader').wasCalledWith('referer'); assertApi('getRequestHeader').wasCalledWith('SP-Anonymous'); assertApi('getRequestHeader').wasCalledWith('origin'); assertApi('setResponseHeader').wasCalledWith( 'Access-Control-Allow-Origin', 'origin' ); assertApi('setResponseHeader').wasCalledWith( 'Access-Control-Allow-Credentials', 'true' ); assertApi('setResponseHeader').wasCalledWith( 'Access-Control-Allow-Headers', 'Content-Type, SP-Anonymous' ); assertApi('setResponseHeader').wasCalledWith( 'Access-Control-Allow-Methods', 'POST, GET, OPTIONS' ); assertApi('returnResponse').wasCalled(); const expectedCommonEvent = { event_name: 'page_view', client_id: 'a4942035-de41-4bbd-b466-9c1ef7f7fb65', language: 'en-GB', page_encoding: 'UTF-8', page_hostname: 'snowplowanalytics.com', page_location: 'https://snowplowanalytics.com/', page_path: '/', page_referrer: 'referer', page_title: 'Collect, manage and operationalize behavioral data at scale | Snowplow', screen_resolution: '1920x1080', user_id: 'snow123', viewport_size: '745x1302', user_agent: 'user-agent', origin: 'origin', host: 'host', 'x-sp-app_id': 'my-app', 'x-sp-platform': 'mob', 'x-sp-dvce_created_tstamp': '1628586512246', 'x-sp-event_id': '8676de79-0eba-4435-ad95-8a41a8a0129c', 'x-sp-name_tracker': 'sp', 'x-sp-v_tracker': 'ios-2.0.0', 'x-sp-domain_sessionid': 'e7580b71-227b-4868-9ea9-322a263ce885', 'x-sp-domain_sessionidx': 1, 'x-sp-domain_userid': 'd54a1904-7798-401a-be0b-1a83bea73634', 'x-sp-user_id': 'snow123', 'x-sp-br_cookies': '1', 'x-sp-br_colordepth': '24', 'x-sp-br_viewwidth': 745, 'x-sp-br_viewheight': 1302, 'x-sp-dvce_screenwidth': 1920, 'x-sp-dvce_screenheight': 1080, 'x-sp-doc_charset': 'UTF-8', 'x-sp-doc_width': 730, 'x-sp-doc_height': 12393, 'x-sp-dvce_sent_tstamp': '1628586512248', 'x-sp-tp2': { e: 'pv', url: 'https://snowplowanalytics.com/', page: 'Collect, manage and operationalize behavioral data at scale | Snowplow', tv: 'ios-2.0.0', tna: 'sp', aid: 'my-app', p: 'mob', tz: 'Europe/London', lang: 'en-GB', cs: 'UTF-8', res: '1920x1080', cd: '24', cookie: '1', eid: '8676de79-0eba-4435-ad95-8a41a8a0129c', dtm: '1628586512246', cx: 'ewogICJzY2hlbWEiOiAiaWdsdTpjb20uc25vd3Bsb3dhbmFseXRpY3Muc25vd3Bsb3cvY29udGV4dHMvanNvbnNjaGVtYS8xLTAtMCIsCiAgImRhdGEiOiBbCiAgICB7CiAgICAgICJzY2hlbWEiOiAiaWdsdTpjb20uc25vd3Bsb3dhbmFseXRpY3Muc25vd3Bsb3cvd2ViX3BhZ2UvanNvbnNjaGVtYS8xLTAtMCIsCiAgICAgICJkYXRhIjogeyAiaWQiOiAiYTg2YzQyZTUtYjgzMS00NWM4LWI3MDYtZTIxNGMyNmI0YjNkIiB9CiAgICB9LAogICAgewogICAgICAic2NoZW1hIjogImlnbHU6b3JnLnczL1BlcmZvcm1hbmNlVGltaW5nL2pzb25zY2hlbWEvMS0wLTAiLAogICAgICAiZGF0YSI6IHsKICAgICAgICAibmF2aWdhdGlvblN0YXJ0IjogMTYyODU4NjUwODYxMCwKICAgICAgICAidW5sb2FkRXZlbnRTdGFydCI6IDAsCiAgICAgICAgInVubG9hZEV2ZW50RW5kIjogMCwKICAgICAgICAicmVkaXJlY3RTdGFydCI6IDAsCiAgICAgICAgInJlZGlyZWN0RW5kIjogMCwKICAgICAgICAiZmV0Y2hTdGFydCI6IDE2Mjg1ODY1MDg2MTAsCiAgICAgICAgImRvbWFpbkxvb2t1cFN0YXJ0IjogMTYyODU4NjUwODYzNywKICAgICAgICAiZG9tYWluTG9va3VwRW5kIjogMTYyODU4NjUwODY5MSwKICAgICAgICAiY29ubmVjdFN0YXJ0IjogMTYyODU4NjUwODY5MSwKICAgICAgICAiY29ubmVjdEVuZCI6IDE2Mjg1ODY1MDg3NjMsCiAgICAgICAgInNlY3VyZUNvbm5lY3Rpb25TdGFydCI6IDE2Mjg1ODY1MDg3MjEsCiAgICAgICAgInJlcXVlc3RTdGFydCI6IDE2Mjg1ODY1MDg3NjMsCiAgICAgICAgInJlc3BvbnNlU3RhcnQiOiAxNjI4NTg2NTA4Nzk3LAogICAgICAgICJyZXNwb25zZUVuZCI6IDE2Mjg1ODY1MDg4MjEsCiAgICAgICAgImRvbUxvYWRpbmciOiAxNjI4NTg2NTA5MDc2LAogICAgICAgICJkb21JbnRlcmFjdGl2ZSI6IDE2Mjg1ODY1MDkzODEsCiAgICAgICAgImRvbUNvbnRlbnRMb2FkZWRFdmVudFN0YXJ0IjogMTYyODU4NjUwOTQwOCwKICAgICAgICAiZG9tQ29udGVudExvYWRlZEV2ZW50RW5kIjogMTYyODU4NjUwOTQxNywKICAgICAgICAiZG9tQ29tcGxldGUiOiAxNjI4NTg2NTEwMzMyLAogICAgICAgICJsb2FkRXZlbnRTdGFydCI6IDE2Mjg1ODY1MTAzMzIsCiAgICAgICAgImxvYWRFdmVudEVuZCI6IDE2Mjg1ODY1MTAzMzQKICAgICAgfQogICAgfSwKICAgIHsKICAgICAgInNjaGVtYSI6ICJpZ2x1OmNvbS5zbm93cGxvd2FuYWx5dGljcy5zbm93cGxvdy9jbGllbnRfc2Vzc2lvbi9qc29uc2NoZW1hLzEtMC0xIiwKICAgICAgImRhdGEiOiB7IAogICAgICAgICJ1c2VySWQiOiAiYTQ5NDIwMzUtZGU0MS00YmJkLWI0NjYtOWMxZWY3ZjdmYjY1IiwKICAgICAgICAic2Vzc2lvbklkIjogImM1OTMzZDU4LWI4YzItNDlkZC1iYWQ1LTYxNTRkNzFhN2I5ZCIsCiAgICAgICAgInNlc3Npb25JbmRleCI6ICI1IgogICAgICB9CiAgICB9CiAgXQp9Cg', vp: '745x1302', ds: '730x12393', vid: '1', sid: 'e7580b71-227b-4868-9ea9-322a263ce885', duid: 'd54a1904-7798-401a-be0b-1a83bea73634', stm: '1628586512248', uid: 'snow123', }, 'x-sp-contexts_com_snowplowanalytics_snowplow_web_page_1': [ { id: 'a86c42e5-b831-45c8-b706-e214c26b4b3d' }, ], 'x-sp-contexts_org_w3_performance_timing_1': [ { navigationStart: 1628586508610, unloadEventStart: 0, unloadEventEnd: 0, redirectStart: 0, redirectEnd: 0, fetchStart: 1628586508610, domainLookupStart: 1628586508637, domainLookupEnd: 1628586508691, connectStart: 1628586508691, connectEnd: 1628586508763, secureConnectionStart: 1628586508721, requestStart: 1628586508763, responseStart: 1628586508797, responseEnd: 1628586508821, domLoading: 1628586509076, domInteractive: 1628586509381, domContentLoadedEventStart: 1628586509408, domContentLoadedEventEnd: 1628586509417, domComplete: 1628586510332, loadEventStart: 1628586510332, loadEventEnd: 1628586510334, }, ], 'x-sp-contexts_com_snowplowanalytics_snowplow_client_session_1': [ { userId: 'a4942035-de41-4bbd-b466-9c1ef7f7fb65', sessionId: 'c5933d58-b8c2-49dd-bad5-6154d71a7b9d', sessionIndex: '5', }, ], ga_session_id: 'c5933d58-b8c2-49dd-bad5-6154d71a7b9d', ga_session_number: '5', 'x-ga-mp2-seg': '1', 'x-ga-protocol_version': '2', 'x-ga-page_id': 'a86c42e5-b831-45c8-b706-e214c26b4b3d', ip_override: '1.2.3.4', }; assertThat(resultingCommonEvent).isEqualTo(expectedCommonEvent); assertApi('runContainer').wasCalledWith( expectedCommonEvent, runContainerCallback ); - name: Container run with enriched event code: | const makeTabMap = require('makeTableMap'); const parseUrl = require('parseUrl'); const funA = (ietfData, event) => { const result = {}; const cookiesMap = makeTabMap(ietfData, 'name', 'value'); const gaCookie = cookiesMap._ga; if (gaCookie) { result.new_client_id = gaCookie.split('.')[0]; } const parsedUrl = parseUrl(event.page_location); const hostname = parsedUrl.hostname.replace('www.', ''); if (hostname === 'snowplowanalytics.com') { result.something_new = 'test_value'; } return result; }; const funB = (linkClickData, event) => { const result = {}; result.target_url = linkClickData.targetUrl; result.element_id = linkClickData.elementId; return result; }; const mockData = { ipInclude: true, populateGaProps: true, serveSpJs: true, customSpJsName: 'example.js', customPostPath: 'custom/path', claimGetRequests: true, includeOriginalTp2Event: true, includeOriginalSelfDescribingEvent: false, includeOriginalContextsArray: false, defaultUserId: true, defaultClientId: true, mergeEntities: true, entityMergeRules: [ { schema: 'contexts_org_schema_web_page_1', versionPolicy: 'control', prefix: '', mergeLevel: 'customPath', customPath: 'foo.bar', keepOriginal: 'keep', customTransformFun: '', }, { schema: 'contexts_org_w3_performance_timing_1', versionPolicy: 'free', prefix: 'x_test_perf_', mergeLevel: 'rootLevel', customPath: '', keepOriginal: 'keep', customTransformFun: '', }, { schema: 'contexts_org_ietf_http_cookie_1', versionPolicy: 'free', prefix: '', mergeLevel: 'rootLevel', customPath: '', keepOriginal: 'discard', customTransformFun: funA, }, ], mergeSelfDesc: true, selfDescMergeRules: [ { schema: 'self_describing_event_com_snowplowanalytics_snowplow_link_click_1', versionPolicy: 'control', prefix: '', mergeLevel: 'customPath', customPath: 'link.click', keepOriginal: 'keep', customTransformFun: funB, }, ], }; const testEvent = enrichedLinkClick; // mocks mock('getRequestPath', '/com.snowplowanalytics.snowplow/enriched'); mock('getRequestMethod', 'POST'); mock('getRequestBody', json.stringify(testEvent)); let runContainerCallback; let resultingCommonEvent; mock('runContainer', (e, cb) => { resultingCommonEvent = e; runContainerCallback = cb; cb(); }); runCode(mockData); assertApi('claimRequest').wasCalled(); assertApi('setResponseStatus').wasCalledWith(200); assertApi('setResponseBody').wasCalledWith('ok'); assertApi('getRequestHeader').wasCalledWith('user-agent'); assertApi('getRequestHeader').wasCalledWith('host'); assertApi('getRequestHeader').wasCalledWith('referer'); assertApi('getRequestHeader').wasCalledWith('SP-Anonymous'); assertApi('getRequestHeader').wasCalledWith('origin'); assertApi('setResponseHeader').wasCalledWith( 'Access-Control-Allow-Origin', 'origin' ); assertApi('setResponseHeader').wasCalledWith( 'Access-Control-Allow-Credentials', 'true' ); assertApi('setResponseHeader').wasCalledWith( 'Access-Control-Allow-Headers', 'Content-Type, SP-Anonymous' ); assertApi('setResponseHeader').wasCalledWith( 'Access-Control-Allow-Methods', 'POST, GET, OPTIONS' ); assertApi('returnResponse').wasCalled(); const expectedCommonEvent = { event_name: 'link_click', client_id: 'bc2e92ec6c204a14', page_hostname: 'www.snowplowanalytics.com', page_location: 'http://www.snowplowanalytics.com', page_path: '/', page_referrer: 'referer', page_title: 'On Analytics', user_id: 'jon.doe@email.com', origin: 'origin', host: 'host', 'x-sp-app_id': 'angry-birds', 'x-sp-platform': 'web', 'x-sp-etl_tstamp': '2017-01-26T00:01:25.292Z', 'x-sp-collector_tstamp': '2013-11-26T00:02:05Z', 'x-sp-dvce_created_tstamp': '2013-11-26T00:03:57.885Z', 'x-sp-event': 'page_view', 'x-sp-event_id': 'c6ef3124-b53a-4b13-a233-0088f79dcbcb', 'x-sp-txn_id': 41828, 'x-sp-name_tracker': 'cloudfront-1', 'x-sp-v_tracker': 'js-2.1.0', 'x-sp-v_collector': 'clj-tomcat-0.1.0', 'x-sp-v_etl': 'serde-0.5.2', 'x-sp-user_fingerprint': '2161814971', 'x-sp-domain_sessionidx': 3, 'x-sp-domain_userid': 'bc2e92ec6c204a14', 'x-sp-user_id': 'jon.doe@email.com', 'x-sp-network_userid': 'ecdff4d0-9175-40ac-a8bb-325c49733607', 'x-sp-geo_country': 'US', 'x-sp-geo_region': 'TX', 'x-sp-geo_city': 'New York', 'x-sp-geo_zipcode': '94109', 'x-sp-geo_latitude': 37.443604, 'x-sp-geo_longitude': -122.4124, 'x-sp-geo_location': '37.443604,-122.4124', 'x-sp-geo_region_name': 'Florida', 'x-sp-ip_isp': 'FDN Communications', 'x-sp-ip_organization': 'Bouygues Telecom', 'x-sp-ip_domain': 'nuvox.net', 'x-sp-ip_netspeed': 'Cable/DSL', 'x-sp-page_urlscheme': 'http', 'x-sp-page_urlhost': 'www.snowplowanalytics.com', 'x-sp-page_urlport': 80, 'x-sp-page_urlpath': '/product/index.html', 'x-sp-page_urlquery': 'id=GTM-DLRG', 'x-sp-page_urlfragment': '4-conclusion', 'x-sp-br_features_pdf': true, 'x-sp-br_features_flash': false, 'x-sp-domain_sessionid': '2b15e5c8-d3b1-11e4-b9d6-1681e6b88ec1', 'x-sp-derived_tstamp': '2013-11-26T00:03:57.886Z', 'x-sp-event_vendor': 'com.snowplowanalytics.snowplow', 'x-sp-event_name': 'link_click', 'x-sp-event_format': 'jsonschema', 'x-sp-event_version': '1-0-0', 'x-sp-event_fingerprint': 'e3dbfa9cca0412c3d4052863cefb547f', 'x-sp-true_tstamp': '2013-11-26T00:03:57.886Z', 'x-sp-contexts_org_schema_web_page_1': [ { genre: 'blog', inLanguage: 'en-US', datePublished: '2014-11-06T00:00:00Z', author: 'Fred Blundun', breadcrumb: ['blog', 'releases'], keywords: ['snowplow', 'javascript', 'tracker', 'event'], }, ], foo: { bar: { genre: 'blog', inLanguage: 'en-US', datePublished: '2014-11-06T00:00:00Z', author: 'Fred Blundun', breadcrumb: ['blog', 'releases'], keywords: ['snowplow', 'javascript', 'tracker', 'event'], }, }, 'x-sp-contexts_org_w3_performance_timing_1': [ { navigationStart: 1415358089861, unloadEventStart: 1415358090270, unloadEventEnd: 1415358090287, redirectStart: 0, redirectEnd: 0, fetchStart: 1415358089870, domainLookupStart: 1415358090102, domainLookupEnd: 1415358090102, connectStart: 1415358090103, connectEnd: 1415358090183, requestStart: 1415358090183, responseStart: 1415358090265, responseEnd: 1415358090265, domLoading: 1415358090270, domInteractive: 1415358090886, domContentLoadedEventStart: 1415358090968, domContentLoadedEventEnd: 1415358091309, domComplete: 0, loadEventStart: 0, loadEventEnd: 0, }, ], x_test_perf_navigationStart: 1415358089861, x_test_perf_unloadEventStart: 1415358090270, x_test_perf_unloadEventEnd: 1415358090287, x_test_perf_redirectStart: 0, x_test_perf_redirectEnd: 0, x_test_perf_fetchStart: 1415358089870, x_test_perf_domainLookupStart: 1415358090102, x_test_perf_domainLookupEnd: 1415358090102, x_test_perf_connectStart: 1415358090103, x_test_perf_connectEnd: 1415358090183, x_test_perf_requestStart: 1415358090183, x_test_perf_responseStart: 1415358090265, x_test_perf_responseEnd: 1415358090265, x_test_perf_domLoading: 1415358090270, x_test_perf_domInteractive: 1415358090886, x_test_perf_domContentLoadedEventStart: 1415358090968, x_test_perf_domContentLoadedEventEnd: 1415358091309, x_test_perf_domComplete: 0, x_test_perf_loadEventStart: 0, x_test_perf_loadEventEnd: 0, 'x-sp-self_describing_event_com_snowplowanalytics_snowplow_link_click_1': { targetUrl: 'http://www.example.com', elementClasses: ['foreground'], elementId: 'exampleLink', }, 'x-sp-contexts_com_snowplowanalytics_snowplow_ua_parser_context_1': [ { useragentFamily: 'IE', useragentMajor: '7', useragentMinor: '0', useragentPatch: null, useragentVersion: 'IE 7.0', osFamily: 'Windows XP', osMajor: null, osMinor: null, osPatch: null, osPatchMinor: null, osVersion: 'Windows XP', deviceFamily: 'Other', }, ], ga_session_id: '2b15e5c8-d3b1-11e4-b9d6-1681e6b88ec1', ga_session_number: '3', 'x-ga-mp2-seg': '1', 'x-ga-protocol_version': '2', ip_override: '92.231.54.234', new_client_id: 'GA1', something_new: 'test_value', link: { click: { target_url: 'http://www.example.com', element_id: 'exampleLink', }, }, }; assertThat(resultingCommonEvent).isEqualTo(expectedCommonEvent); assertApi('runContainer').wasCalledWith( expectedCommonEvent, runContainerCallback ); - name: Container run with advanced common event settings code: | const mockData = { ipInclude: true, populateGaProps: true, serveSpJs: true, customSpJsName: 'sp.js', customPostPath: '/com.snowplowanalytics.snowplow/tp2', claimGetRequests: true, includeOriginalTp2Event: true, includeOriginalSelfDescribingEvent: true, includeOriginalContextsArray: true, defaultClientId: false, clientId: [ { priority: '10', propPath: 'x-sp-domain_userid', }, { priority: '20', propPath: 'x-sp-contexts_com_snowplowanalytics_snowplow_client_session_1.0.firstEventId', }, ], defaultUserId: false, userId: [ { priority: '10', propPath: 'x-sp-contexts_com_google_tag-manager_server-side_user_data_1.0.email_address', }, ], mergeEntities: false, mergeSelfDesc: false, }; const testEvent = mediaEventTp2; // mocks mock('getRequestPath', '/com.snowplowanalytics.snowplow/tp2'); mock('getRequestMethod', 'POST'); mock('getRequestBody', json.stringify(testEvent)); let runContainerCallback; let resultingCommonEvent; mock('runContainer', (e, cb) => { resultingCommonEvent = e; runContainerCallback = cb; cb(); }); runCode(mockData); assertApi('claimRequest').wasCalled(); assertApi('returnResponse').wasCalled(); const selfDescEvent = mediaEventTp2.data[0]; const expectedCommonEvent = { event_name: 'media_player_event', language: 'en-US', page_encoding: 'windows-1252', page_hostname: 'localhost', page_location: 'http://localhost:8000/', page_path: '/', page_referrer: 'referer', screen_resolution: '1920x1080', viewport_size: '744x971', user_agent: 'user-agent', origin: 'origin', host: 'host', ip_override: '1.2.3.4', 'x-sp-app_id': 'media-test', 'x-sp-platform': 'web', 'x-sp-dvce_created_tstamp': '1697609091769', 'x-sp-event_id': '011c85b9-0ee1-4f01-a9ca-7bd11db0c811', 'x-sp-name_tracker': 'spTest', 'x-sp-v_tracker': 'js-3.16.0', 'x-sp-domain_sessionid': '6ce287c3-e58d-4501-9586-1062d9b2d80c', 'x-sp-domain_sessionidx': 1, 'x-sp-domain_userid': 'fd97960a-bcb9-4530-8446-e370e1952e5e', 'x-sp-user_id': 'media_tester', 'x-sp-br_cookies': '1', 'x-sp-br_colordepth': '24', 'x-sp-br_viewwidth': 744, 'x-sp-br_viewheight': 971, 'x-sp-dvce_screenwidth': 1920, 'x-sp-dvce_screenheight': 1080, 'x-sp-doc_charset': 'windows-1252', 'x-sp-doc_width': 729, 'x-sp-doc_height': 1211, 'x-sp-dvce_sent_tstamp': '1697609091773', 'x-sp-tp2': selfDescEvent, 'x-sp-self_describing_event_com_snowplowanalytics_snowplow_media_player_event_1': { type: 'pause' }, 'x-sp-self_describing_event': json.parse(selfDescEvent.ue_pr).data, 'x-sp-contexts': json.parse(selfDescEvent.co).data, 'x-sp-contexts_org_whatwg_media_element_1': [ { htmlId: 'bunny-mp4', mediaType: 'VIDEO', autoPlay: false, buffered: [{ start: 0, end: 13.424 }], controls: true, currentSrc: 'https://archive.org/download/BigBuckBunny_124/Content/big_buck_bunny_720p_surround.mp4', defaultMuted: false, defaultPlaybackRate: 1, error: null, networkState: 'NETWORK_LOADING', preload: 'metadata', readyState: 'HAVE_ENOUGH_DATA', seekable: [{ start: 0, end: 596.48 }], seeking: false, src: 'https://archive.org/download/BigBuckBunny_124/Content/big_buck_bunny_720p_surround.mp4', textTracks: [], fileExtension: 'mp4', fullscreen: false, pictureInPicture: false, }, ], 'x-sp-contexts_com_snowplowanalytics_snowplow_media_player_1': [ { currentTime: 5.801593, duration: 596.48, ended: false, loop: false, muted: false, paused: true, playbackRate: 1, volume: 100, }, ], 'x-sp-contexts_org_whatwg_video_element_1': [ { poster: '', videoHeight: 360, videoWidth: 640 }, ], 'x-sp-contexts_com_snowplowanalytics_snowplow_web_page_1': [ { id: '586b753d-c961-4852-a164-f641c9a4404f' }, ], 'x-sp-contexts_com_google_tag-manager_server-side_user_data_1': [ { email_address: 'foo@example.com', phone_number: '+15551234567', address: { first_name: 'Jane', last_name: 'Doe', street: '123 Fake St', city: 'San Francisco', region: 'CA', postal_code: '94016', country: 'US', }, }, ], 'x-sp-contexts_com_snowplowanalytics_snowplow_mobile_context_1': [ { osType: 'testOsType', osVersion: 'testOsVersion', deviceManufacturer: 'testDevMan', deviceModel: 'testDevModel', }, ], 'x-sp-contexts_com_snowplowanalytics_snowplow_client_session_1': [ { userId: 'fd97960a-bcb9-4530-8446-e370e1952e5e', sessionId: '6ce287c3-e58d-4501-9586-1062d9b2d80c', eventIndex: 4, sessionIndex: 1, previousSessionId: null, storageMechanism: 'COOKIE_1', firstEventId: 'f8525402-1483-4fc4-8fc7-3eea2559127e', firstEventTimestamp: '2023-10-18T06:04:39.898Z', }, ], user_data: { email_address: 'foo@example.com', phone_number: '+15551234567', address: { first_name: 'Jane', last_name: 'Doe', street: '123 Fake St', city: 'San Francisco', region: 'CA', postal_code: '94016', country: 'US', }, }, ga_session_id: '6ce287c3-e58d-4501-9586-1062d9b2d80c', ga_session_number: '1', 'x-ga-mp2-seg': '1', 'x-ga-protocol_version': '2', 'x-ga-page_id': '586b753d-c961-4852-a164-f641c9a4404f', client_id: 'f8525402-1483-4fc4-8fc7-3eea2559127e', user_id: 'foo@example.com', }; assertThat(resultingCommonEvent).isEqualTo(expectedCommonEvent); assertApi('runContainer').wasCalledWith( expectedCommonEvent, runContainerCallback ); setup: |- const json = require('JSON'); const log = require('logToConsole'); const storage = require('templateDataStorage'); mock('getRequestHeader', (header) => { if (header === 'SP-Anonymous') { return undefined; } return header; }); mock('getRemoteAddress', '1.2.3.4'); mock('getCookieValues', (c) => { return [c]; }); mock('claimRequest', function () {}); const page_view_tp2 = { schema: 'iglu:com.snowplowanalytics.snowplow/payload_data/jsonschema/1-0-4', data: [ { e: 'pv', url: 'https://snowplowanalytics.com/', page: 'Collect, manage and operationalize behavioral data at scale | Snowplow', tv: 'js-2.18.1', tna: 'sp', aid: 'website', p: 'web', tz: 'Europe/London', lang: 'en-GB', cs: 'UTF-8', res: '1920x1080', cd: '24', cookie: '1', eid: '8676de79-0eba-4435-ad95-8a41a8a0129c', dtm: '1628586512246', cx: 'eyJzY2hlbWEiOiJpZ2x1OmNvbS5zbm93cGxvd2FuYWx5dGljcy5zbm93cGxvdy9jb250ZXh0cy9qc29uc2NoZW1hLzEtMC0wIiwiZGF0YSI6W3sic2NoZW1hIjoiaWdsdTpjb20uc25vd3Bsb3dhbmFseXRpY3Muc25vd3Bsb3cvd2ViX3BhZ2UvanNvbnNjaGVtYS8xLTAtMCIsImRhdGEiOnsiaWQiOiJhODZjNDJlNS1iODMxLTQ1YzgtYjcwNi1lMjE0YzI2YjRiM2QifX0seyJzY2hlbWEiOiJpZ2x1Om9yZy53My9QZXJmb3JtYW5jZVRpbWluZy9qc29uc2NoZW1hLzEtMC0wIiwiZGF0YSI6eyJuYXZpZ2F0aW9uU3RhcnQiOjE2Mjg1ODY1MDg2MTAsInVubG9hZEV2ZW50U3RhcnQiOjAsInVubG9hZEV2ZW50RW5kIjowLCJyZWRpcmVjdFN0YXJ0IjowLCJyZWRpcmVjdEVuZCI6MCwiZmV0Y2hTdGFydCI6MTYyODU4NjUwODYxMCwiZG9tYWluTG9va3VwU3RhcnQiOjE2Mjg1ODY1MDg2MzcsImRvbWFpbkxvb2t1cEVuZCI6MTYyODU4NjUwODY5MSwiY29ubmVjdFN0YXJ0IjoxNjI4NTg2NTA4NjkxLCJjb25uZWN0RW5kIjoxNjI4NTg2NTA4NzYzLCJzZWN1cmVDb25uZWN0aW9uU3RhcnQiOjE2Mjg1ODY1MDg3MjEsInJlcXVlc3RTdGFydCI6MTYyODU4NjUwODc2MywicmVzcG9uc2VTdGFydCI6MTYyODU4NjUwODc5NywicmVzcG9uc2VFbmQiOjE2Mjg1ODY1MDg4MjEsImRvbUxvYWRpbmciOjE2Mjg1ODY1MDkwNzYsImRvbUludGVyYWN0aXZlIjoxNjI4NTg2NTA5MzgxLCJkb21Db250ZW50TG9hZGVkRXZlbnRTdGFydCI6MTYyODU4NjUwOTQwOCwiZG9tQ29udGVudExvYWRlZEV2ZW50RW5kIjoxNjI4NTg2NTA5NDE3LCJkb21Db21wbGV0ZSI6MTYyODU4NjUxMDMzMiwibG9hZEV2ZW50U3RhcnQiOjE2Mjg1ODY1MTAzMzIsImxvYWRFdmVudEVuZCI6MTYyODU4NjUxMDMzNH19XX0', vp: '745x1302', ds: '730x12393', vid: '1', sid: 'e7580b71-227b-4868-9ea9-322a263ce885', duid: 'd54a1904-7798-401a-be0b-1a83bea73634', stm: '1628586512248', }, ], }; const mediaEventTp2 = { schema: 'iglu:com.snowplowanalytics.snowplow/payload_data/jsonschema/1-0-4', data: [ { e: 'ue', eid: '011c85b9-0ee1-4f01-a9ca-7bd11db0c811', tv: 'js-3.16.0', tna: 'spTest', aid: 'media-test', p: 'web', cookie: '1', cs: 'windows-1252', lang: 'en-US', res: '1920x1080', cd: '24', tz: 'Europe/Athens', dtm: '1697609091769', vp: '744x971', ds: '729x1211', vid: '1', sid: '6ce287c3-e58d-4501-9586-1062d9b2d80c', duid: 'fd97960a-bcb9-4530-8446-e370e1952e5e', uid: 'media_tester', url: 'http://localhost:8000/', ue_pr: '{"schema":"iglu:com.snowplowanalytics.snowplow/unstruct_event/jsonschema/1-0-0","data":{"schema":"iglu:com.snowplowanalytics.snowplow/media_player_event/jsonschema/1-0-0","data":{"type":"pause"}}}', co: '{"schema":"iglu:com.snowplowanalytics.snowplow/contexts/jsonschema/1-0-0","data":[{"schema":"iglu:org.whatwg/media_element/jsonschema/1-0-0","data":{"htmlId":"bunny-mp4","mediaType":"VIDEO","autoPlay":false,"buffered":[{"start":0,"end":13.424}],"controls":true,"currentSrc":"https://archive.org/download/BigBuckBunny_124/Content/big_buck_bunny_720p_surround.mp4","defaultMuted":false,"defaultPlaybackRate":1,"error":null,"networkState":"NETWORK_LOADING","preload":"metadata","readyState":"HAVE_ENOUGH_DATA","seekable":[{"start":0,"end":596.48}],"seeking":false,"src":"https://archive.org/download/BigBuckBunny_124/Content/big_buck_bunny_720p_surround.mp4","textTracks":[],"fileExtension":"mp4","fullscreen":false,"pictureInPicture":false}},{"schema":"iglu:com.snowplowanalytics.snowplow/media_player/jsonschema/1-0-0","data":{"currentTime":5.801593,"duration":596.48,"ended":false,"loop":false,"muted":false,"paused":true,"playbackRate":1,"volume":100}},{"schema":"iglu:org.whatwg/video_element/jsonschema/1-0-0","data":{"poster":"","videoHeight":360,"videoWidth":640}},{"schema":"iglu:com.snowplowanalytics.snowplow/web_page/jsonschema/1-0-0","data":{"id":"586b753d-c961-4852-a164-f641c9a4404f"}},{"schema":"iglu:com.google.tag-manager.server-side/user_data/jsonschema/1-0-0","data":{"email_address":"foo@example.com","phone_number":"+15551234567","address":{"first_name":"Jane","last_name":"Doe","street":"123 Fake St","city":"San Francisco","region":"CA","postal_code":"94016","country":"US"}}},{"schema":"iglu:com.snowplowanalytics.snowplow/mobile_context/jsonschema/1-0-2","data":{"osType":"testOsType","osVersion":"testOsVersion","deviceManufacturer":"testDevMan","deviceModel":"testDevModel"}},{"schema":"iglu:com.snowplowanalytics.snowplow/client_session/jsonschema/1-0-2","data":{"userId":"fd97960a-bcb9-4530-8446-e370e1952e5e","sessionId":"6ce287c3-e58d-4501-9586-1062d9b2d80c","eventIndex":4,"sessionIndex":1,"previousSessionId":null,"storageMechanism":"COOKIE_1","firstEventId":"f8525402-1483-4fc4-8fc7-3eea2559127e","firstEventTimestamp":"2023-10-18T06:04:39.898Z"}}]}', stm: '1697609091773', }, ], }; const page_view_mobile = { schema: 'iglu:com.snowplowanalytics.snowplow/payload_data/jsonschema/1-0-4', data: [ { e: 'pv', url: 'https://snowplowanalytics.com/', page: 'Collect, manage and operationalize behavioral data at scale | Snowplow', tv: 'ios-2.0.0', tna: 'sp', aid: 'my-app', p: 'mob', tz: 'Europe/London', lang: 'en-GB', cs: 'UTF-8', res: '1920x1080', cd: '24', cookie: '1', eid: '8676de79-0eba-4435-ad95-8a41a8a0129c', dtm: '1628586512246', cx: 'ewogICJzY2hlbWEiOiAiaWdsdTpjb20uc25vd3Bsb3dhbmFseXRpY3Muc25vd3Bsb3cvY29udGV4dHMvanNvbnNjaGVtYS8xLTAtMCIsCiAgImRhdGEiOiBbCiAgICB7CiAgICAgICJzY2hlbWEiOiAiaWdsdTpjb20uc25vd3Bsb3dhbmFseXRpY3Muc25vd3Bsb3cvd2ViX3BhZ2UvanNvbnNjaGVtYS8xLTAtMCIsCiAgICAgICJkYXRhIjogeyAiaWQiOiAiYTg2YzQyZTUtYjgzMS00NWM4LWI3MDYtZTIxNGMyNmI0YjNkIiB9CiAgICB9LAogICAgewogICAgICAic2NoZW1hIjogImlnbHU6b3JnLnczL1BlcmZvcm1hbmNlVGltaW5nL2pzb25zY2hlbWEvMS0wLTAiLAogICAgICAiZGF0YSI6IHsKICAgICAgICAibmF2aWdhdGlvblN0YXJ0IjogMTYyODU4NjUwODYxMCwKICAgICAgICAidW5sb2FkRXZlbnRTdGFydCI6IDAsCiAgICAgICAgInVubG9hZEV2ZW50RW5kIjogMCwKICAgICAgICAicmVkaXJlY3RTdGFydCI6IDAsCiAgICAgICAgInJlZGlyZWN0RW5kIjogMCwKICAgICAgICAiZmV0Y2hTdGFydCI6IDE2Mjg1ODY1MDg2MTAsCiAgICAgICAgImRvbWFpbkxvb2t1cFN0YXJ0IjogMTYyODU4NjUwODYzNywKICAgICAgICAiZG9tYWluTG9va3VwRW5kIjogMTYyODU4NjUwODY5MSwKICAgICAgICAiY29ubmVjdFN0YXJ0IjogMTYyODU4NjUwODY5MSwKICAgICAgICAiY29ubmVjdEVuZCI6IDE2Mjg1ODY1MDg3NjMsCiAgICAgICAgInNlY3VyZUNvbm5lY3Rpb25TdGFydCI6IDE2Mjg1ODY1MDg3MjEsCiAgICAgICAgInJlcXVlc3RTdGFydCI6IDE2Mjg1ODY1MDg3NjMsCiAgICAgICAgInJlc3BvbnNlU3RhcnQiOiAxNjI4NTg2NTA4Nzk3LAogICAgICAgICJyZXNwb25zZUVuZCI6IDE2Mjg1ODY1MDg4MjEsCiAgICAgICAgImRvbUxvYWRpbmciOiAxNjI4NTg2NTA5MDc2LAogICAgICAgICJkb21JbnRlcmFjdGl2ZSI6IDE2Mjg1ODY1MDkzODEsCiAgICAgICAgImRvbUNvbnRlbnRMb2FkZWRFdmVudFN0YXJ0IjogMTYyODU4NjUwOTQwOCwKICAgICAgICAiZG9tQ29udGVudExvYWRlZEV2ZW50RW5kIjogMTYyODU4NjUwOTQxNywKICAgICAgICAiZG9tQ29tcGxldGUiOiAxNjI4NTg2NTEwMzMyLAogICAgICAgICJsb2FkRXZlbnRTdGFydCI6IDE2Mjg1ODY1MTAzMzIsCiAgICAgICAgImxvYWRFdmVudEVuZCI6IDE2Mjg1ODY1MTAzMzQKICAgICAgfQogICAgfSwKICAgIHsKICAgICAgInNjaGVtYSI6ICJpZ2x1OmNvbS5zbm93cGxvd2FuYWx5dGljcy5zbm93cGxvdy9jbGllbnRfc2Vzc2lvbi9qc29uc2NoZW1hLzEtMC0xIiwKICAgICAgImRhdGEiOiB7IAogICAgICAgICJ1c2VySWQiOiAiYTQ5NDIwMzUtZGU0MS00YmJkLWI0NjYtOWMxZWY3ZjdmYjY1IiwKICAgICAgICAic2Vzc2lvbklkIjogImM1OTMzZDU4LWI4YzItNDlkZC1iYWQ1LTYxNTRkNzFhN2I5ZCIsCiAgICAgICAgInNlc3Npb25JbmRleCI6ICI1IgogICAgICB9CiAgICB9CiAgXQp9Cg', vp: '745x1302', ds: '730x12393', vid: '1', sid: 'e7580b71-227b-4868-9ea9-322a263ce885', duid: 'd54a1904-7798-401a-be0b-1a83bea73634', stm: '1628586512248', uid: 'snow123', }, ], }; const enrichedLinkClick = { geo_location: '37.443604,-122.4124', app_id: 'angry-birds', platform: 'web', etl_tstamp: '2017-01-26T00:01:25.292Z', collector_tstamp: '2013-11-26T00:02:05Z', dvce_created_tstamp: '2013-11-26T00:03:57.885Z', event: 'page_view', event_id: 'c6ef3124-b53a-4b13-a233-0088f79dcbcb', txn_id: 41828, name_tracker: 'cloudfront-1', v_tracker: 'js-2.1.0', v_collector: 'clj-tomcat-0.1.0', v_etl: 'serde-0.5.2', user_id: 'jon.doe@email.com', user_ipaddress: '92.231.54.234', user_fingerprint: '2161814971', domain_userid: 'bc2e92ec6c204a14', domain_sessionidx: 3, network_userid: 'ecdff4d0-9175-40ac-a8bb-325c49733607', geo_country: 'US', geo_region: 'TX', geo_city: 'New York', geo_zipcode: '94109', geo_latitude: 37.443604, geo_longitude: -122.4124, geo_region_name: 'Florida', ip_isp: 'FDN Communications', ip_organization: 'Bouygues Telecom', ip_domain: 'nuvox.net', ip_netspeed: 'Cable/DSL', page_url: 'http://www.snowplowanalytics.com', page_title: 'On Analytics', page_referrer: null, page_urlscheme: 'http', page_urlhost: 'www.snowplowanalytics.com', page_urlport: 80, page_urlpath: '/product/index.html', page_urlquery: 'id=GTM-DLRG', page_urlfragment: '4-conclusion', refr_urlscheme: null, refr_urlhost: null, refr_urlport: null, refr_urlpath: null, refr_urlquery: null, refr_urlfragment: null, refr_medium: null, refr_source: null, refr_term: null, mkt_medium: null, mkt_source: null, mkt_term: null, mkt_content: null, mkt_campaign: null, contexts_org_ietf_http_cookie_1: [ { name: '_ga', value: 'GA1.2.3' }, { name: '_ga_FOO', value: 'GS1.2.3' }, ], contexts_org_schema_web_page_1: [ { genre: 'blog', inLanguage: 'en-US', datePublished: '2014-11-06T00:00:00Z', author: 'Fred Blundun', breadcrumb: ['blog', 'releases'], keywords: ['snowplow', 'javascript', 'tracker', 'event'], }, ], contexts_org_w3_performance_timing_1: [ { navigationStart: 1415358089861, unloadEventStart: 1415358090270, unloadEventEnd: 1415358090287, redirectStart: 0, redirectEnd: 0, fetchStart: 1415358089870, domainLookupStart: 1415358090102, domainLookupEnd: 1415358090102, connectStart: 1415358090103, connectEnd: 1415358090183, requestStart: 1415358090183, responseStart: 1415358090265, responseEnd: 1415358090265, domLoading: 1415358090270, domInteractive: 1415358090886, domContentLoadedEventStart: 1415358090968, domContentLoadedEventEnd: 1415358091309, domComplete: 0, loadEventStart: 0, loadEventEnd: 0, }, ], se_category: null, se_action: null, se_label: null, se_property: null, se_value: null, unstruct_event_com_snowplowanalytics_snowplow_link_click_1: { targetUrl: 'http://www.example.com', elementClasses: ['foreground'], elementId: 'exampleLink', }, tr_orderid: null, tr_affiliation: null, tr_total: null, tr_tax: null, tr_shipping: null, tr_city: null, tr_state: null, tr_country: null, ti_orderid: null, ti_sku: null, ti_name: null, ti_category: null, ti_price: null, ti_quantity: null, pp_xoffset_min: null, pp_xoffset_max: null, pp_yoffset_min: null, pp_yoffset_max: null, useragent: null, br_name: null, br_family: null, br_version: null, br_type: null, br_renderengine: null, br_lang: null, br_features_pdf: true, br_features_flash: false, br_features_java: null, br_features_director: null, br_features_quicktime: null, br_features_realplayer: null, br_features_windowsmedia: null, br_features_gears: null, br_features_silverlight: null, br_cookies: null, br_colordepth: null, br_viewwidth: null, br_viewheight: null, os_name: null, os_family: null, os_manufacturer: null, os_timezone: null, dvce_type: null, dvce_ismobile: null, dvce_screenwidth: null, dvce_screenheight: null, doc_charset: null, doc_width: null, doc_height: null, tr_currency: null, tr_total_base: null, tr_tax_base: null, tr_shipping_base: null, ti_currency: null, ti_price_base: null, base_currency: null, geo_timezone: null, mkt_clickid: null, mkt_network: null, etl_tags: null, dvce_sent_tstamp: null, refr_domain_userid: null, refr_dvce_tstamp: null, contexts_com_snowplowanalytics_snowplow_ua_parser_context_1: [ { useragentFamily: 'IE', useragentMajor: '7', useragentMinor: '0', useragentPatch: null, useragentVersion: 'IE 7.0', osFamily: 'Windows XP', osMajor: null, osMinor: null, osPatch: null, osPatchMinor: null, osVersion: 'Windows XP', deviceFamily: 'Other', }, ], domain_sessionid: '2b15e5c8-d3b1-11e4-b9d6-1681e6b88ec1', derived_tstamp: '2013-11-26T00:03:57.886Z', event_vendor: 'com.snowplowanalytics.snowplow', event_name: 'link_click', event_format: 'jsonschema', event_version: '1-0-0', event_fingerprint: 'e3dbfa9cca0412c3d4052863cefb547f', true_tstamp: '2013-11-26T00:03:57.886Z', }; ___NOTES___ Created on 24/07/2020, 15:17:13