diff --git a/modules/kargoBidAdapter.js b/modules/kargoBidAdapter.js index 1ba0f392d08..4f594c2856f 100644 --- a/modules/kargoBidAdapter.js +++ b/modules/kargoBidAdapter.js @@ -3,6 +3,8 @@ import {config} from 'src/config'; import {registerBidder} from 'src/adapters/bidderFactory'; const BIDDER_CODE = 'kargo'; const HOST = 'https://krk.kargo.com'; +const SYNC = 'https://crb.kargo.com/api/v1/initsyncrnd/{UUID}?seed={SEED}&idx={INDEX}'; +const SYNC_COUNT = 5; export const spec = { code: BIDDER_CODE, isBidRequestValid: function(bid) { @@ -54,6 +56,20 @@ export const spec = { } return bidResponses; }, + getUserSyncs: function(syncOptions) { + const syncs = []; + const seed = spec._generateRandomUuid(); + const clientId = spec._getClientId(); + if (syncOptions.iframeEnabled && seed && clientId) { + for (let i = 0; i < SYNC_COUNT; i++) { + syncs.push({ + type: 'iframe', + url: SYNC.replace('{UUID}', clientId).replace('{SEED}', seed).replace('{INDEX}', i) + }); + } + } + return syncs; + }, // PRIVATE _readCookie(name) { @@ -150,6 +166,11 @@ export const spec = { }; }, + _getClientId() { + const uid = spec._getUid(); + return uid.clientId; + }, + _getAllMetadata() { return { userIDs: spec._getUserIds(), @@ -157,6 +178,22 @@ export const spec = { pageURL: window.location.href, rawCRB: spec._readCookie('krg_crb') }; + }, + + _generateRandomUuid() { + try { + // crypto.getRandomValues is supported everywhere but Opera Mini for years + var buffer = new Uint8Array(16); + crypto.getRandomValues(buffer); + buffer[6] = (buffer[6] & ~176) | 64; + buffer[8] = (buffer[8] & ~64) | 128; + var hex = Array.prototype.map.call(new Uint8Array(buffer), function(x) { + return ('00' + x.toString(16)).slice(-2); + }).join(''); + return hex.slice(0, 8) + '-' + hex.slice(8, 12) + '-' + hex.slice(12, 16) + '-' + hex.slice(16, 20) + '-' + hex.slice(20); + } catch (e) { + return ''; + } } }; registerBidder(spec); diff --git a/test/spec/modules/kargoBidAdapter_spec.js b/test/spec/modules/kargoBidAdapter_spec.js index eafb5a9c0f3..c60d74e040a 100644 --- a/test/spec/modules/kargoBidAdapter_spec.js +++ b/test/spec/modules/kargoBidAdapter_spec.js @@ -387,4 +387,97 @@ describe('kargo adapter tests', function () { expect(resp).to.deep.equal(expectation); }); }); + + describe('user sync handler', function() { + const clientId = '74c81cbb-7d07-46d9-be9b-68ccb291c949'; + var shouldSimulateOutdatedBrowser, uid, isActuallyOutdatedBrowser; + + beforeEach(() => { + uid = {}; + shouldSimulateOutdatedBrowser = false; + isActuallyOutdatedBrowser = false; + + // IE11 fails these tests in the Prebid test suite. Since this + // browser won't support any of this stuff we expect all user + // syncing to fail gracefully. Kargo is mobile only, so this + // doesn't really matter. + if (!window.crypto) { + isActuallyOutdatedBrowser = true; + } else { + sandbox.stub(crypto, 'getRandomValues').callsFake(function(buf) { + if (shouldSimulateOutdatedBrowser) { + throw new Error('Could not generate random values'); + } + var bytes = [50, 5, 232, 133, 141, 55, 49, 57, 244, 126, 248, 44, 255, 38, 128, 0]; + for (var i = 0; i < bytes.length; i++) { + buf[i] = bytes[i]; + } + return buf; + }); + } + + sandbox.stub(spec, '_getUid').callsFake(function() { + return uid; + }); + }); + + function getUserSyncsWhenAllowed() { + return spec.getUserSyncs({iframeEnabled: true}); + } + + function getUserSyncsWhenForbidden() { + return spec.getUserSyncs({}); + } + + function turnOnClientId() { + uid.clientId = clientId; + } + + function simulateOutdatedBrowser() { + shouldSimulateOutdatedBrowser = true; + } + + function getSyncUrl(index) { + return { + type: 'iframe', + url: `https://crb.kargo.com/api/v1/initsyncrnd/${clientId}?seed=3205e885-8d37-4139-b47e-f82cff268000&idx=${index}` + }; + } + + function getSyncUrls() { + var syncs = []; + for (var i = 0; i < 5; i++) { + syncs[i] = getSyncUrl(i); + } + return syncs; + } + + function safelyRun(runExpectation) { + if (isActuallyOutdatedBrowser) { + expect(getUserSyncsWhenAllowed()).to.be.an('array').that.is.empty; + } else { + runExpectation(); + } + } + + it('handles user syncs when there is a client id', function() { + turnOnClientId(); + safelyRun(() => expect(getUserSyncsWhenAllowed()).to.deep.equal(getSyncUrls())); + }); + + it('no user syncs when there is no client id', function() { + safelyRun(() => expect(getUserSyncsWhenAllowed()).to.be.an('array').that.is.empty); + }); + + it('no user syncs when there is outdated browser', function() { + turnOnClientId(); + simulateOutdatedBrowser(); + safelyRun(() => expect(getUserSyncsWhenAllowed()).to.be.an('array').that.is.empty); + }); + + it('no user syncs when no iframe syncing allowed', function() { + turnOnClientId(); + safelyRun(() => expect(getUserSyncsWhenForbidden()).to.be.an('array').that.is.empty); + }); + }); });