___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": "TAG", "id": "cvt_temp_public_id", "version": 1, "securityGroups": [], "displayName": "FingerprintJS Pro", "categories": [ "SESSION_RECORDING" ], "brand": { "id": "brand_dummy", "displayName": "", "thumbnail": "data:image/png;base64,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\u003d" }, "description": "This template adds FingerprintJS Pro agent, the highest accuracy device identification. Visit https://fingerprint.com/ for more information and get you public API key.", "containerContexts": [ "WEB" ] } ___TEMPLATE_PARAMETERS___ [ { "type": "TEXT", "name": "apiKey", "simpleValueType": true, "displayName": "Public API key", "valueValidators": [ { "type": "NON_EMPTY" } ], "help": "Your public API key that authenticates the agent with the API" }, { "type": "SELECT", "name": "region", "displayName": "Region", "macrosInSelect": false, "selectItems": [ { "value": "", "displayValue": "Global" }, { "value": "eu", "displayValue": "EU" }, { "value": "ap", "displayValue": "Asia (Mumbai)" } ], "simpleValueType": true, "help": "Which region to use" }, { "type": "GROUP", "name": "additionalFields", "displayName": "Additional Settings", "groupStyle": "ZIPPY_CLOSED", "subParams": [ { "type": "TEXT", "name": "scriptUrlPattern", "displayName": "Script Url Pattern", "simpleValueType": true, "help": "Pattern of the JS agent script URL." }, { "type": "TEXT", "name": "endpoint", "displayName": "Endpoint", "simpleValueType": true, "help": "Server API URL. Should be only used with Subdomain integration." }, { "type": "TEXT", "name": "tag", "displayName": "tag", "simpleValueType": true, "help": "A customer-provided value or an object that will be saved together with the identification event" }, { "type": "TEXT", "name": "linkedId", "displayName": "linkedId", "simpleValueType": true, "help": "A way of linking current identification event with a custom identifier" }, { "type": "CHECKBOX", "name": "extendedResult", "checkboxText": "Extended result", "simpleValueType": true }, { "type": "TEXT", "name": "resultCustomName", "displayName": "Result custom name", "simpleValueType": true, "help": "You can change result variable name that emits in dataLayer", "defaultValue": "FingerprintJSProResult", "valueValidators": [ { "type": "NON_EMPTY" } ] } ] } ] ___SANDBOXED_JS_FOR_WEB_TEMPLATE___ // Enter your template code here. const log = require('logToConsole'); const injectScript = require('injectScript'); const queryPermission = require('queryPermission'); const callInWindow = require('callInWindow'); const createQueue = require('createQueue'); const copyFromWindow = require('copyFromWindow'); const url = 'https://opencdn.fpjs.sh/fingerprintjs-pro-gtm/v0/iife.min.js'; log('data =', data); const loadOptions = { apiKey: data.apiKey, integrationInfo: 'fingerprintjs-pro-gtm-template/0.0.1', }; if (data.region) { loadOptions.region = data.region; } if (data.endpoint) { loadOptions.endpoint = data.endpoint; } if (data.scriptUrlPattern) { loadOptions.scriptUrlPattern = data.scriptUrlPattern; } const getOptions = {}; if (data.tag) { getOptions.tag = data.tag; } if (data.linkedId) { getOptions.linkedId = data.linkedId; } if (data.extendedResult) { getOptions.extendedResult = true; } log('loadOptions =', loadOptions); log('getOptions=', getOptions); const onSuccess = () => { log('FingerprintJS: Script loaded successfully.'); const onFpJsLoad = (result) => { log('result', result); const dataLayerPush = createQueue('dataLayer'); const event = {event: 'FingerprintJSPro.loaded'}; event[data.resultCustomName] = result; dataLayerPush(event); data.gtmOnSuccess(); }; callInWindow('FingerprintjsProGTM.load', loadOptions, getOptions, onFpJsLoad); }; // If the script fails to load, log a message and signal failure const onFailure = () => { log('FingerprintJS: Script load failed.'); data.gtmOnFailure(); }; if (queryPermission('inject_script', url) && queryPermission('access_globals', 'execute', 'FingerprintjsProGTM.load')) { log('try to load'); injectScript(url, onSuccess, onFailure); } else { log('FingerprintJS: Script load failed due to permissions mismatch.'); data.gtmOnFailure(); } ___WEB_PERMISSIONS___ [ { "instance": { "key": { "publicId": "logging", "versionId": "1" }, "param": [ { "key": "environments", "value": { "type": 1, "string": "debug" } } ] }, "clientAnnotations": { "isEditedByUser": true }, "isRequired": true }, { "instance": { "key": { "publicId": "inject_script", "versionId": "1" }, "param": [ { "key": "urls", "value": { "type": 2, "listItem": [ { "type": 1, "string": "https://opencdn.fpjs.sh/fingerprintjs-pro-gtm/v0/iife.min.js" } ] } } ] }, "clientAnnotations": { "isEditedByUser": true }, "isRequired": true }, { "instance": { "key": { "publicId": "access_globals", "versionId": "1" }, "param": [ { "key": "keys", "value": { "type": 2, "listItem": [ { "type": 3, "mapKey": [ { "type": 1, "string": "key" }, { "type": 1, "string": "read" }, { "type": 1, "string": "write" }, { "type": 1, "string": "execute" } ], "mapValue": [ { "type": 1, "string": "FingerprintjsProGTM.load" }, { "type": 8, "boolean": false }, { "type": 8, "boolean": false }, { "type": 8, "boolean": true } ] }, { "type": 3, "mapKey": [ { "type": 1, "string": "key" }, { "type": 1, "string": "read" }, { "type": 1, "string": "write" }, { "type": 1, "string": "execute" } ], "mapValue": [ { "type": 1, "string": "dataLayer" }, { "type": 8, "boolean": true }, { "type": 8, "boolean": true }, { "type": 8, "boolean": false } ] } ] } } ] }, "clientAnnotations": { "isEditedByUser": true }, "isRequired": true } ] ___TESTS___ scenarios: - name: check loading code: |- const mockData = { apiKey: 'aspodkasodk', }; mock('injectScript', function(url, onSuccess, onFail) { onSuccess(); }); mock('callInWindow', function(fname, getOptions, loadOptions, callback) { callback({visitorId: 'qwerty'}); }); // Call runCode to run the template's code. runCode(mockData); assertApi('injectScript').wasCalled(); assertApi('createQueue').wasCalled(); // Verify that the tag finished successfully. assertApi('gtmOnSuccess').wasCalled(); - name: check loadOptions ang getOptions with minimum params code: |- const mockData = { apiKey: 'aspodkasodk', }; const expectedLoadOptions = { apiKey: 'aspodkasodk', integrationInfo: 'fingerprintjs-pro-gtm-template/0.0.1', }; mock('injectScript', function(url, onSuccess, onFail) { onSuccess(); }); mock('callInWindow', function(fname, loadOptions, getOptions, callback) { assertThat(loadOptions).isEqualTo(expectedLoadOptions); assertThat(getOptions).isEqualTo({}); callback({visitorId: 'qwerty'}); }); // Call runCode to run the template's code. runCode(mockData); assertApi('gtmOnSuccess').wasCalled(); - name: check loadOptions ang getOptions with maximum params code: |- const mockData = { apiKey: 'aspodkasodk', region: 'eu', scriptUrlPattern: 'https://domain.some/v//loader_v.js', endpoint: 'https://end.point/', tag: 'myTag', linkedId: 'some-id', extendedResult: true, }; const expectedLoadOptions = { apiKey: 'aspodkasodk', integrationInfo: 'fingerprintjs-pro-gtm-template/0.0.1', region: 'eu', endpoint: 'https://end.point/', scriptUrlPattern: 'https://domain.some/v//loader_v.js', }; const expectedGetOptions = { tag: 'myTag', linkedId: 'some-id', extendedResult: true, }; mock('injectScript', function(url, onSuccess, onFail) { onSuccess(); }); mock('callInWindow', function(fname, loadOptions, getOptions, callback) { assertThat(loadOptions).isEqualTo(expectedLoadOptions); assertThat(getOptions).isEqualTo(expectedGetOptions); callback({visitorId: 'qwerty'}); }); // Call runCode to run the template's code. runCode(mockData); assertApi('gtmOnSuccess').wasCalled(); - name: check visitorIdCustomName field works code: |- const mockData = { apiKey: 'aspodkasodk', visitorIdCustomName: 'fingerprintJsProVisitorId', resultCustomName: 'result' }; mock('injectScript', function(url, onSuccess, onFail) { onSuccess(); }); mock('callInWindow', function(fname, getOptions, loadOptions, callback) { callback({visitorId: 'qwerty'}); }); mock('createQueue', function(name) { return function(params) { assertThat(params).isEqualTo({ event: 'FingerprintJSPro.loaded', result: {visitorId: 'qwerty'}, }); }; }); // Call runCode to run the template's code. runCode(mockData); // Verify that the tag finished successfully. assertApi('gtmOnSuccess').wasCalled(); ___NOTES___ Created on 30.03.2022, 20:54:30