From fa30e971263240dd6266fb110d4bec91de71f9f1 Mon Sep 17 00:00:00 2001 From: Philip Watson Date: Tue, 12 Nov 2024 08:58:39 +1300 Subject: [PATCH 001/123] StroeerCore Bid Adapter: add the ortb2 site extension to the request (#12433) --- modules/stroeerCoreBidAdapter.js | 2 +- .../modules/stroeerCoreBidAdapter_spec.js | 22 +++++++++++++++++++ 2 files changed, 23 insertions(+), 1 deletion(-) diff --git a/modules/stroeerCoreBidAdapter.js b/modules/stroeerCoreBidAdapter.js index 072b72a6724..fedebc72053 100644 --- a/modules/stroeerCoreBidAdapter.js +++ b/modules/stroeerCoreBidAdapter.js @@ -75,7 +75,7 @@ export const spec = { }; } - const ORTB2_KEYS = ['regs.ext.dsa', 'device.ext.cdep']; + const ORTB2_KEYS = ['regs.ext.dsa', 'device.ext.cdep', 'site.ext']; ORTB2_KEYS.forEach(key => { const value = deepAccess(bidderRequest.ortb2, key); if (value !== undefined) { diff --git a/test/spec/modules/stroeerCoreBidAdapter_spec.js b/test/spec/modules/stroeerCoreBidAdapter_spec.js index 3cd9626885d..89ebf485ca0 100644 --- a/test/spec/modules/stroeerCoreBidAdapter_spec.js +++ b/test/spec/modules/stroeerCoreBidAdapter_spec.js @@ -935,6 +935,28 @@ describe('stroeerCore bid adapter', function () { assert.deepEqual(serverRequestInfo.data.bids[0].sfp, {}); assert.isUndefined(serverRequestInfo.data.bids[1].sfp); }); + + it('should add the ortb2 site extension', () => { + const bidReq = buildBidderRequest(); + + const ortb2 = { + site: { + domain: 'example.com', + ext: { + data: { + abc: '123' + } + } + } + }; + + bidReq.ortb2 = utils.deepClone(ortb2); + + const serverRequestInfo = spec.buildRequests(bidReq.bids, bidReq); + + const sentOrtb2 = serverRequestInfo.data.ortb2; + assert.deepEqual(sentOrtb2, {site: {ext: ortb2.site.ext}}) + }); }); }); }); From 805714ca4bc1372f39e992d6c8f961cfca01d1d0 Mon Sep 17 00:00:00 2001 From: Patrick McCann Date: Mon, 11 Nov 2024 15:12:54 -0500 Subject: [PATCH 002/123] CadentApertureMX Bid Adapter : remove bidder code validation (#12404) * Update cadentApertureMXBidAdapter.js * Update cadentApertureMXBidAdapter_spec.js * Update cadentApertureMXBidAdapter_spec.js * Update cadentApertureMXBidAdapter_spec.js --- modules/cadentApertureMXBidAdapter.js | 5 --- .../cadentApertureMXBidAdapter_spec.js | 38 ------------------- 2 files changed, 43 deletions(-) diff --git a/modules/cadentApertureMXBidAdapter.js b/modules/cadentApertureMXBidAdapter.js index 97283952888..fa441a4f4fa 100644 --- a/modules/cadentApertureMXBidAdapter.js +++ b/modules/cadentApertureMXBidAdapter.js @@ -230,11 +230,6 @@ export const spec = { return false; } - if (bid.bidder !== BIDDER_CODE) { - logWarn(BIDDER_CODE + ': Must use "cadent_aperture_mx" as bidder code.'); - return false; - } - if (!bid.params.tagid || !isStr(bid.params.tagid)) { logWarn(BIDDER_CODE + ': Missing tagid param or tagid present and not type String.'); return false; diff --git a/test/spec/modules/cadentApertureMXBidAdapter_spec.js b/test/spec/modules/cadentApertureMXBidAdapter_spec.js index 4f0f2cf8f20..d8686e3c667 100644 --- a/test/spec/modules/cadentApertureMXBidAdapter_spec.js +++ b/test/spec/modules/cadentApertureMXBidAdapter_spec.js @@ -48,49 +48,11 @@ describe('cadent_aperture_mx Adapter', function () { 'auctionId': '1d1a01234a475' }; let noBid = {}; - let otherBid = { - 'bidder': 'emxdigital', - 'params': { - 'tagid': '25251' - }, - 'mediaTypes': { - 'banner': { - 'sizes': [[300, 250]] - } - }, - 'adUnitCode': 'adunit-code', - 'sizes': [ - [300, 250], - [300, 600] - ], - 'bidId': '30b31c2501de1e', - 'bidderRequestId': '22edbae3120bf6', - 'auctionId': '1d1a01234a475' - }; - let noMediaSizeBid = { - 'bidder': 'emxdigital', - 'params': { - 'tagid': '25251' - }, - 'mediaTypes': { - 'banner': {} - }, - 'adUnitCode': 'adunit-code', - 'sizes': [ - [300, 250], - [300, 600] - ], - 'bidId': '30b31c2501de1e', - 'bidderRequestId': '22edbae3120bf6', - 'auctionId': '1d1a01234a475' - }; it('should return true when required params found', function () { expect(spec.isBidRequestValid(bid)).to.equal(true); expect(spec.isBidRequestValid(badBid)).to.equal(false); expect(spec.isBidRequestValid(noBid)).to.equal(false); - expect(spec.isBidRequestValid(otherBid)).to.equal(false); - expect(spec.isBidRequestValid(noMediaSizeBid)).to.equal(false); }); }); From e16c01fe490371b6679234550b33665b7bffdaa8 Mon Sep 17 00:00:00 2001 From: James Rosewell Date: Tue, 12 Nov 2024 15:57:13 +0000 Subject: [PATCH 003/123] 51Degrees RTD submodule: optimise ORTB2 enrichment speed (#12394) * 51Degrees RTD submodule: optimise ORTB2 enrichment speed by eliminating redundant call * 51Degrees RTD submodule: mock HEV --------- Co-authored-by: Bohdan V <25197509+BohdanVV@users.noreply.github.com> --- .../gpt/51DegreesRtdProvider_example.html | 7 +- modules/51DegreesRtdProvider.js | 78 +++++++++---- .../spec/modules/51DegreesRtdProvider_spec.js | 106 +++++++++++++++++- 3 files changed, 159 insertions(+), 32 deletions(-) diff --git a/integrationExamples/gpt/51DegreesRtdProvider_example.html b/integrationExamples/gpt/51DegreesRtdProvider_example.html index 5f66dd9c8e2..7864f2e05f5 100644 --- a/integrationExamples/gpt/51DegreesRtdProvider_example.html +++ b/integrationExamples/gpt/51DegreesRtdProvider_example.html @@ -92,7 +92,7 @@ name: '51Degrees', waitForIt: true, params: { - // Get your resource key from https://configure.51degrees.com/tWrhNfY6 + // Get your resource key from https://configure.51degrees.com/HNZ75HT1 resourceKey: '', // alternatively, you can use the on-premise version of the 51Degrees service and connect to your chosen end point // onPremiseJSUrl: 'https://localhost/51Degrees.core.js' @@ -181,12 +181,11 @@

div-banner-native-2

Testing/Debugging Guidance

  1. Make sure you have debug: true under pbjs.setConfig in this example code (be sure to remove it for production!) -
  2. Make sure you have replaced <YOUR RESOURCE KEY> in this example code with the one you have obtained - from the 51Degrees Configurator Tool
  3. +
  4. Make sure you have replaced <YOUR RESOURCE KEY> in this example code with the one you have obtained + from the 51Degrees Configurator Tool
  5. Open DevTools Console in your browser and refresh the page
  6. Observe the enriched ortb device data shown below and also in the console as part of the [51Degrees RTD Submodule]: reqBidsConfigObj: message (under reqBidsConfigObj.global.device)
- - \ No newline at end of file + diff --git a/modules/brainxBidAdapter.js b/modules/brainxBidAdapter.js new file mode 100644 index 00000000000..69832987fb7 --- /dev/null +++ b/modules/brainxBidAdapter.js @@ -0,0 +1,117 @@ +import { deepAccess, generateUUID, isArray, logWarn } from '../src/utils.js'; +import { registerBidder } from '../src/adapters/bidderFactory.js'; +// import { config } from 'src/config.js'; +import { BANNER } from '../src/mediaTypes.js'; +import { ortbConverter } from '../libraries/ortbConverter/converter.js' +// import { config } from '../src/config.js'; + +const BIDDER_CODE = 'brainx'; +const METHOD = 'POST'; +const TTL = 200; +const NET_REV = true; +let ENDPOINT = 'https://dsp.brainx.tech/bid' +// let ENDPOINT = 'http://adx-engine-gray.tec-do.cn/bid' + +const converter = ortbConverter({ + context: { + // `netRevenue` and `ttl` are required properties of bid responses - provide a default for them + netRevenue: NET_REV, // or false if your adapter should set bidResponse.netRevenue = false + ttl: TTL // default bidResponse.ttl (when not specified in ORTB response.seatbid[].bid[].exp) + } +}); + +export const spec = { + code: BIDDER_CODE, + // gvlid: IAB_GVL_ID, + // aliases: [ + // { code: "myalias", gvlid: IAB_GVL_ID_IF_DIFFERENT } + // ], + isBidRequestValid: function (bid) { + if (!(hasBanner(bid) || hasVideo(bid))) { + logWarn('Invalid bid request - missing required mediaTypes'); + return false; + } + if (!(bid && bid.params)) { + logWarn('Invalid bid request - missing required bid data'); + return false; + } + + if (!(bid.params.pubId)) { + logWarn('Invalid bid request - missing required field pubId'); + return false; + } + return true; + }, + buildRequests(bidRequests, bidderRequest) { + const data = converter.toORTB({ bidRequests, bidderRequest }) + ENDPOINT = String(deepAccess(bidRequests[0], 'params.endpoint')) ? deepAccess(bidRequests[0], 'params.endpoint') : ENDPOINT + data.user = { + buyeruid: generateUUID() + } + return { + method: METHOD, + url: `${ENDPOINT}?token=${String(deepAccess(bidRequests[0], 'params.pubId'))}`, + data + } + }, + interpretResponse(response, request) { + let bids = []; + if (response.body && response.body.seatbid && isArray(response.body.seatbid)) { + response.body.seatbid.forEach(function (bidder) { + if (isArray(bidder.bid)) { + bidder.bid.map((bid) => { + let serverBody = response.body; + // bidRequest = request.originalBidRequest, + let mediaType = BANNER; + let currency = serverBody.cur || 'USD' + + const cpm = (parseFloat(bid.price) || 0).toFixed(2); + const categories = deepAccess(bid, 'cat', []); + + const bidRes = { + ad: bid.adm, + width: bid.w, + height: bid.h, + requestId: bid.impid, + cpm: cpm, + currency: currency, + mediaType: mediaType, + ttl: TTL, + creativeId: bid.crid || bid.id, + netRevenue: NET_REV, + nurl: bid.nurl, + lurl: bid.lurl, + meta: { + mediaType: mediaType, + primaryCatId: categories[0], + secondaryCatIds: categories.slice(1), + } + }; + if (bid.adomain && isArray(bid.adomain) && bid.adomain.length > 0) { + bidRes.meta.advertiserDomains = bid.adomain; + bidRes.meta.clickUrl = bid.adomain[0]; + } + bids.push(bidRes); + }) + } + }); + } + + return bids; + }, + // getUserSyncs: function (syncOptions, serverResponses, gdprConsent, uspConsent) { }, + // onTimeout: function (timeoutData) { }, + // onBidWon: function (bid) { }, + // onSetTargeting: function (bid) { }, + // onBidderError: function ({ error, bidderRequest }) { }, + // onAdRenderSucceeded: function (bid) { }, + supportedMediaTypes: [BANNER] +} +function hasBanner(bidRequest) { + return !!deepAccess(bidRequest, 'mediaTypes.banner'); +} +function hasVideo(bidRequest) { + return !!deepAccess(bidRequest, 'mediaTypes.video'); +} + +registerBidder(spec); diff --git a/modules/brainxBidAdapter.md b/modules/brainxBidAdapter.md new file mode 100644 index 00000000000..a734147321b --- /dev/null +++ b/modules/brainxBidAdapter.md @@ -0,0 +1,56 @@ +# brianx Bidder Adapter + +## Overview + +``` +Module Name: brianx Bidder Adapter +Module Type: Bidder Adapter +Maintainer: brainx.official@tec-do.com +``` + +## Description + +Module that connects to brianx's demand sources + +## Bid Parameters + +| Name | Scope | Type | Description | Example | +| ---------- | ------------ | ------ | ------------------------------------ | -------------------------------------- | +| `pubId` | required | String | The Pub Id provided by Brainx Ads. | `F7B53DBC-85C1-4685-9A06-9CF4B6261FA3` | +| `endpoint` | optional | String | The endpoint provided by Brainx Url. | `https://dsp.brainx.tech/bid` | + +## Example + +### Banner Ads + +```javascript +var adUnits = [{ + code: 'banner-ad-div', + mediaTypes: { + banner: { + sizes: [ + [320, 250], + [320, 480] + ] + } + }, + bids: [{ + bidder: 'brianx', + params: { + pubId: 'F7B53DBC-85C1-4685-9A06-9CF4B6261FA3', + endpoint: 'https://dsp.brainx.tech/bid' + } + }] +}]; +``` + +* For video ads, enable prebid cache. + +```javascript +pbjs.setConfig({ + ortb2: { + ortbVersion: '2.5' + }, + debug: false // or true +}); +``` diff --git a/test/spec/modules/brainxBidAdapter_spec.js b/test/spec/modules/brainxBidAdapter_spec.js new file mode 100644 index 00000000000..1d01e2cc642 --- /dev/null +++ b/test/spec/modules/brainxBidAdapter_spec.js @@ -0,0 +1,132 @@ +// import or require modules necessary for the test, e.g.: +import { expect } from 'chai'; // may prefer 'assert' in place of 'expect' +import { spec } from 'modules/brainxBidAdapter.js'; +import utils, { deepClone } from '../../../src/utils'; +// import adapter from 'src/adapters/'; +import { BANNER, NATIVE, VIDEO } from 'src/mediaTypes.js'; + +describe('Brain-X Aapater', function () { + describe('isBidRequestValid', function () { + it('undefined bid should return false', function () { + expect(spec.isBidRequestValid()).to.be.false; + }); + + it('null bid should return false', function () { + expect(spec.isBidRequestValid(null)).to.be.false; + }); + + it('bid.params should be set', function () { + expect(spec.isBidRequestValid({})).to.be.false; + }); + + it('bid.params.pubId should be set', function () { + expect(spec.isBidRequestValid({ + params: { pubId: 'F7B53DBC-85C1-4685-9A06-9CF4B6261FA3', endpoint: 'http://adx-engine-gray.tec-do.cn/bid' } + })).to.be.false; + }); + }) + + // describe('isBidRequestValid', function () { + // it('Test the banner request processing function', function () { + // const request = spec.buildRequests(bannerRequest, bannerRequest[0]); + // expect(request).to.not.be.empty; + // const payload = request.data; + // expect(payload).to.not.be.empty; + // }); + // it('Test the video request processing function', function () { + // const request = spec.buildRequests(videoRequest, videoRequest[0]); + // expect(request).to.not.be.empty; + // const payload = request.data; + // expect(payload).to.not.be.empty; + // }); + // it('Test the param', function () { + // const request = spec.buildRequests(bannerRequest, bannerRequest[0]); + // const payload = JSON.parse(request.data); + // expect(payload.imp[0].tagid).to.eql(videoRequest[0].params.tagid); + // expect(payload.imp[0].bidfloor).to.eql(videoRequest[0].params.bidfloor); + // }); + // }) + + describe('interpretResponse', function () { + it('Test banner interpretResponse', function () { + const serverResponse = { + body: { + 'bidid': 'a82042c055b04e539ec6876112c10ced1729663902983', + 'cur': 'USD', + 'id': '28f8f1f525372a', + 'seatbid': [ + { + 'bid': [ + { + 'adid': '76797', + 'adm': "
\n \n
\n
\n \n \n
\n
\n\n", + 'adomain': [ + 'taobao.com' + ], + 'bundle': 'com.taobao', + 'burl': 'https://adx-event-server.bidtrail.top/billing?s=Eid0ZWMxNDk4NDQzODk3MjcwOTU1MzAwNzM3YjczMTQwMTA4ODk5ODQaDjI4ZjhmMWY1MjUzNzJhIKkCKgbmtYvor5U6DmxvY2FsaG9zdDo5OTk5Qgl1bmRlZmluZWRKA0hLR1ADYAFoAXADeAOAAQKKAQpjb20udGFvYmFvlQEX2U49nQEX2U49pQEX2U49ugEqCO4HEiUI7gcSCFRlY2RvRFNQGA01F9lOPTgBQBVKC1RlY2RvRFNQX1NHwAHuB8gBjcqCwKsy0AEC-gGbAmh0dHBzOi8vbm90aWNlLXNnLmJpZHRyYWlsLnRvcC93aW4_YmlkX2lkPWE4MjA0MmMwNTViMDRlNTM5ZWM2ODc2MTEyYzEwY2VkMTcyOTY2MzkwMjk4MyZjYW1wYWlnbl9pZD00MjgmYWRfZ3JvdXBfaWQ9OTc2MSZhZF9pZD03Njc5NyZjcmVhdGl2ZV9pZD02NTI2JmFmZmlsaWF0ZV9pZD0yOTcmYnVuZGxlPSZmaXJzdF9zc3A9YnJhaW54LnRlY2gmcHVibGlzaF9pZD0mb3M9QW5kcm9pZCZ0YWdfaWQ9dW5kZWZpbmVkJmJpZF9zdWNjZXNzX3ByaWNlPTAuMDUwNSZzaWduPWUzODQyNjhiYzAzYzhmYmOSAgp0YW9iYW8uY29togILYnJhaW54LnRlY2ioAgGyAgdhbmRyb2lkwAICygICc2fQAgHYAo3hlcWrMuACjeGVxasy&v=P-1b7JJWs-uUQ68A37V4xDLplU0&auction_price=${AUCTION_PRICE}', + 'cat': [ + 'IAB18-5' + ], + 'cid': '428', + 'crid': 'D06g1RVGMEnC+9Le4SZMJw==', + 'h': 480, + 'id': 'a82042c055b04e539ec6876112c10ced1729663902983', + 'impid': '3c1dd9e1700358', + 'iurl': 'https://creative.bidtrail.top/png/2/20f24c10f21e/091b422e3014033e57acffcf2a5c71dbb17383ec15ac9421', + 'lurl': 'https://notice-sg.bidtrail.top/loss?bid_id=a82042c055b04e539ec6876112c10ced1729663902983&sign=e384268bc03c8fbc&campaign_id=428&ad_group_id=9761&ad_id=76797&creative_id=6526&affiliate_id=297&loss_code=${AUCTION_LOSS}', + 'nurl': 'https://adx-event-server.bidtrail.top/winnotice?s=Eid0ZWMxNDk4NDQzODk3MjcwOTU1MzAwNzM3YjczMTQwMTA4ODk5ODQaDjI4ZjhmMWY1MjUzNzJhIKkCKgbmtYvor5U6DmxvY2FsaG9zdDo5OTk5Qgl1bmRlZmluZWRKA0hLR1ADYAFoAXADeAOAAQKKAQpjb20udGFvYmFvlQEX2U49nQEX2U49pQEX2U49ugEqCO4HEiUI7gcSCFRlY2RvRFNQGA01F9lOPTgBQBVKC1RlY2RvRFNQX1NHwAHuB8gBjcqCwKsy0AEB-gGbAmh0dHBzOi8vbm90aWNlLXNnLmJpZHRyYWlsLnRvcC93aW4_YmlkX2lkPWE4MjA0MmMwNTViMDRlNTM5ZWM2ODc2MTEyYzEwY2VkMTcyOTY2MzkwMjk4MyZjYW1wYWlnbl9pZD00MjgmYWRfZ3JvdXBfaWQ9OTc2MSZhZF9pZD03Njc5NyZjcmVhdGl2ZV9pZD02NTI2JmFmZmlsaWF0ZV9pZD0yOTcmYnVuZGxlPSZmaXJzdF9zc3A9YnJhaW54LnRlY2gmcHVibGlzaF9pZD0mb3M9QW5kcm9pZCZ0YWdfaWQ9dW5kZWZpbmVkJmJpZF9zdWNjZXNzX3ByaWNlPTAuMDUwNSZzaWduPWUzODQyNjhiYzAzYzhmYmOSAgp0YW9iYW8uY29togILYnJhaW54LnRlY2ioAgGyAgdhbmRyb2lkwAICygICc2fQAgHYAo3hlcWrMuACjeGVxasy&v=jFUg_b6F14d50JL-M6UE5Jc8VuA&auction_price=${AUCTION_PRICE}', + 'price': 0.0505, + 'w': 320 + } + ], + 'group': 0, + 'seat': 'agency' + } + ] + } + }; + + const bidResponses = spec.interpretResponse(serverResponse, { + originalBidRequest: { + auctionId: '3eedbf83-7d1d-423c-be27-39e4af687040', + auctionStart: 1729663900819, + adUnitCode: 'dev-1', + bidId: '28f8f1f525372a', + bidder: 'brainx', + mediaTypes: { banner: { sizes: [[300, 250]] } }, + params: { + pubId: 'F7B53DBC-85C1-4685-9A06-9CF4B6261FA3', + endpoint: 'http://adx-engine-gray.tec-do.cn/bid' + }, + src: 'client' + } + }); + + expect(bidResponses).to.be.an('array').that.is.not.empty; + + const bid = serverResponse.body.seatbid[0].bid[0]; + const bidResponse = bidResponses[0]; + + expect(bidResponse.mediaType).to.equal(BANNER); + expect(bidResponse.requestId).to.equal(bid.impid); + expect(bidResponse.cpm).to.equal(parseFloat(bid.price).toFixed(2)) + expect(bidResponse.currency).to.equal(serverResponse.body.cur); + expect(bidResponse.creativeId).to.equal(bid.crid || bid.id); + expect(bidResponse.netRevenue).to.be.true; + expect(bidResponse.nurl).to.equal(bid.nurl); + expect(bidResponse.lurl).to.equal(bid.lurl); + + expect(bidResponse.meta).to.be.an('object'); + expect(bidResponse.meta.mediaType).to.equal(BANNER); + expect(bidResponse.meta.primaryCatId).to.equal('IAB18-5'); + // expect(bidResponse.meta.secondaryCatIds).to.deep.equal(['IAB8']); + expect(bidResponse.meta.advertiserDomains).to.deep.equal(bid.adomain); + expect(bidResponse.meta.clickUrl).to.equal(bid.adomain[0]); + + expect(bidResponse.ad).to.equal(bid.adm); + expect(bidResponse.width).to.equal(bid.w); + expect(bidResponse.height).to.equal(bid.h); + }); + }); +}); From d3f3696d59df559d659f596214b65cac8da1cff9 Mon Sep 17 00:00:00 2001 From: Demetrio Girardi Date: Wed, 18 Dec 2024 04:37:34 -0800 Subject: [PATCH 119/123] dfpAdServerVideo: set vconp (continuous playblack on) when playbackmethod contains 7 (#12590) --- modules/dfpAdServerVideo.js | 2 +- test/spec/modules/dfpAdServerVideo_spec.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/modules/dfpAdServerVideo.js b/modules/dfpAdServerVideo.js index 87ead9fe980..773b3896270 100644 --- a/modules/dfpAdServerVideo.js +++ b/modules/dfpAdServerVideo.js @@ -133,7 +133,7 @@ export function buildDfpVideoUrl(options) { return 'preroll'; } }, - vconp: () => Array.isArray(video?.playbackmethod) && video.playbackmethod.every(m => m === 7) ? '2' : undefined, + vconp: () => Array.isArray(video?.playbackmethod) && video.playbackmethod.some(m => m === 7) ? '2' : undefined, vpa() { // playbackmethod = 3 is play on click; 1, 2, 4, 5, 6 are autoplay if (Array.isArray(video?.playbackmethod)) { diff --git a/test/spec/modules/dfpAdServerVideo_spec.js b/test/spec/modules/dfpAdServerVideo_spec.js index 092cd1ff0f3..75765771d1a 100644 --- a/test/spec/modules/dfpAdServerVideo_spec.js +++ b/test/spec/modules/dfpAdServerVideo_spec.js @@ -271,7 +271,7 @@ describe('The DFP video support module', function () { video: { playbackmethod: [7, 1] }, - expected: undefined + expected: '2' } ], vpa: [ From cc2e8d20e9113082d72d9130d11d113913bfd67f Mon Sep 17 00:00:00 2001 From: Demetrio Girardi Date: Wed, 18 Dec 2024 10:18:18 -0800 Subject: [PATCH 120/123] PBS Adapter: only include known bidders in eidpermissions (#12594) --- .../prebidServerBidAdapter/bidderConfig.js | 9 +++++--- .../prebidServerBidAdapter/ortbConverter.js | 2 +- .../modules/prebidServerBidAdapter_spec.js | 21 +++++++++++++++++-- 3 files changed, 26 insertions(+), 6 deletions(-) diff --git a/modules/prebidServerBidAdapter/bidderConfig.js b/modules/prebidServerBidAdapter/bidderConfig.js index 44ab2f90d42..3b51810ff20 100644 --- a/modules/prebidServerBidAdapter/bidderConfig.js +++ b/modules/prebidServerBidAdapter/bidderConfig.js @@ -121,7 +121,7 @@ export function consolidateEids({eids, conflicts = new Set()}) { } } -function replaceEids({global, bidder}) { +function replaceEids({global, bidder}, requestedBidders) { const consolidated = consolidateEids(extractEids({global, bidder})); global = deepClone(global); bidder = deepClone(bidder); @@ -134,6 +134,9 @@ function replaceEids({global, bidder}) { if (consolidated.global.length) { deepSetValue(global, 'user.ext.eids', consolidated.global); } + if (requestedBidders?.length) { + consolidated.permissions.forEach((permission) => permission.bidders = permission.bidders.filter(bidder => requestedBidders.includes(bidder))); + } if (consolidated.permissions.length) { deepSetValue(global, 'ext.prebid.data.eidpermissions', consolidated.permissions); } @@ -145,11 +148,11 @@ function replaceEids({global, bidder}) { return {global, bidder} } -export function premergeFpd(ortb2Fragments) { +export function premergeFpd(ortb2Fragments, requestedBidders) { if (ortb2Fragments == null || Object.keys(ortb2Fragments.bidder || {}).length === 0) { return ortb2Fragments; } else { - ortb2Fragments = replaceEids(ortb2Fragments); + ortb2Fragments = replaceEids(ortb2Fragments, requestedBidders); return { ...ortb2Fragments, bidder: getPBSBidderConfig(ortb2Fragments) diff --git a/modules/prebidServerBidAdapter/ortbConverter.js b/modules/prebidServerBidAdapter/ortbConverter.js index 2cc34586102..51a01e04fdd 100644 --- a/modules/prebidServerBidAdapter/ortbConverter.js +++ b/modules/prebidServerBidAdapter/ortbConverter.js @@ -299,7 +299,7 @@ export function buildPBSRequest(s2sBidRequest, bidderRequests, adUnits, requeste requestTimestamp, s2sBidRequest: { ...s2sBidRequest, - ortb2Fragments: premergeFpd(s2sBidRequest.ortb2Fragments) + ortb2Fragments: premergeFpd(s2sBidRequest.ortb2Fragments, requestedBidders) }, requestedBidders, actualBidderRequests: bidderRequests, diff --git a/test/spec/modules/prebidServerBidAdapter_spec.js b/test/spec/modules/prebidServerBidAdapter_spec.js index 058175b878c..85ae7909f34 100644 --- a/test/spec/modules/prebidServerBidAdapter_spec.js +++ b/test/spec/modules/prebidServerBidAdapter_spec.js @@ -2115,6 +2115,23 @@ describe('S2S Adapter', function () { }]); }); + it('should not set eidpermissions for unrequested bidders', () => { + req.ortb2Fragments.bidder.unknown = { + user: { + eids: [{source: 'idC', id: 3}, {source: 'idD', id: 4}] + } + } + adapter.callBids(req, BID_REQUESTS, addBidResponse, done, ajax); + const payload = JSON.parse(server.requests[0].requestBody); + expect(payload.ext.prebid.data.eidpermissions).to.eql([{ + bidders: ['appnexus'], + source: 'idC' + }, { + bidders: [], + source: 'idD' + }]); + }) + it('should repeat global EIDs when bidder-specific EIDs conflict', () => { BID_REQUESTS.push({ ...BID_REQUESTS[0], @@ -4710,8 +4727,8 @@ describe('S2S Adapter', function () { bidder: { bidderA: [mkEid('idA', 'idA2')] } - }) - }) + }); + }); }) }); }); From cc3e707267a80548166487d68d40179a15cbf03b Mon Sep 17 00:00:00 2001 From: vivekyadav15 Date: Thu, 19 Dec 2024 00:21:05 +0530 Subject: [PATCH 121/123] Bug fix: Export getProperties method (#12587) --- src/auction.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/auction.js b/src/auction.js index 3945e0bfe53..c7ccaa27b14 100644 --- a/src/auction.js +++ b/src/auction.js @@ -415,7 +415,8 @@ export function newAuction({adUnits, adUnitCodes, callback, cbTimeout, labels, a getFPD: () => ortb2Fragments, getMetrics: () => metrics, end: done.promise, - requestsDone: requestsDone.promise + requestsDone: requestsDone.promise, + getProperties }; } From 8162df6dff7fb038f38b4c3e4736f5042e71fd4a Mon Sep 17 00:00:00 2001 From: Jurij Sinickij Date: Wed, 18 Dec 2024 20:51:53 +0200 Subject: [PATCH 122/123] Adf Bid Adapter: use common ortb2 data (#12582) * use common ortb2 data from bidderRequest * set iab categories on bid response --- modules/adfBidAdapter.js | 56 +++++------- test/spec/modules/adfBidAdapter_spec.js | 117 +++++++++++------------- 2 files changed, 75 insertions(+), 98 deletions(-) diff --git a/modules/adfBidAdapter.js b/modules/adfBidAdapter.js index dc2cab498ea..70cd526065c 100644 --- a/modules/adfBidAdapter.js +++ b/modules/adfBidAdapter.js @@ -33,7 +33,7 @@ export const spec = { let app, site; const commonFpd = bidderRequest.ortb2 || {}; - let { user } = commonFpd; + let user = commonFpd.user || {}; if (typeof getConfig('app') === 'object') { app = getConfig('app') || {}; @@ -51,21 +51,35 @@ export const spec = { } } - const device = getConfig('device') || {}; + let device = getConfig('device') || {}; + if (commonFpd.device) { + mergeDeep(device, commonFpd.device); + } device.w = device.w || window.innerWidth; device.h = device.h || window.innerHeight; device.ua = device.ua || navigator.userAgent; + let source = commonFpd.source || {}; + source.fd = 1; + + let regs = commonFpd.regs || {}; + const adxDomain = setOnAny(validBidRequests, 'params.adxDomain') || 'adx.adform.net'; const pt = setOnAny(validBidRequests, 'params.pt') || setOnAny(validBidRequests, 'params.priceType') || 'net'; - const tid = bidderRequest.ortb2?.source?.tid; const test = setOnAny(validBidRequests, 'params.test'); const currency = getCurrencyFromBidderRequest(bidderRequest); const cur = currency && [ currency ]; const eids = setOnAny(validBidRequests, 'userIdAsEids'); const schain = setOnAny(validBidRequests, 'schain'); - const dsa = commonFpd.regs?.ext?.dsa; + + if (eids) { + deepSetValue(user, 'ext.eids', eids); + } + + if (schain) { + deepSetValue(source, 'ext.schain', schain); + } const imp = validBidRequests.map((bid, id) => { bid.netRevenue = pt; @@ -151,10 +165,11 @@ export const spec = { app, user, device, - source: { tid, fd: 1 }, + source, ext: { pt }, cur, - imp + imp, + regs }; if (test) { @@ -162,31 +177,6 @@ export const spec = { request.test = 1; } - if (config.getConfig('coppa')) { - deepSetValue(request, 'regs.coppa', 1); - } - - if (deepAccess(bidderRequest, 'gdprConsent.gdprApplies') !== undefined) { - deepSetValue(request, 'user.ext.consent', bidderRequest.gdprConsent.consentString); - deepSetValue(request, 'regs.ext.gdpr', bidderRequest.gdprConsent.gdprApplies & 1); - } - - if (bidderRequest.uspConsent) { - deepSetValue(request, 'regs.ext.us_privacy', bidderRequest.uspConsent); - } - - if (eids) { - deepSetValue(request, 'user.ext.eids', eids); - } - - if (schain) { - deepSetValue(request, 'source.ext.schain', schain); - } - - if (dsa) { - deepSetValue(request, 'regs.ext.dsa', dsa); - } - return { method: 'POST', url: 'https://' + adxDomain + '/adx/openrtb', @@ -224,7 +214,9 @@ export const spec = { meta: { mediaType, advertiserDomains: bidResponse.adomain, - dsa + dsa, + primaryCatId: bidResponse.cat?.[0], + secondaryCatIds: bidResponse.cat?.slice(1) } }; diff --git a/test/spec/modules/adfBidAdapter_spec.js b/test/spec/modules/adfBidAdapter_spec.js index 14abb33ba68..e5a2c9e76b5 100644 --- a/test/spec/modules/adfBidAdapter_spec.js +++ b/test/spec/modules/adfBidAdapter_spec.js @@ -82,33 +82,45 @@ describe('Adf adapter', function () { }); describe('user privacy', function () { - it('should send GDPR Consent data to adform if gdprApplies', function () { + it('should send GDPR Consent data to adform', function () { let validBidRequests = [{ bidId: 'bidId', params: { test: 1 } }]; - let bidderRequest = { gdprConsent: { gdprApplies: true, consentString: 'consentDataString' }, refererInfo: { page: 'page' } }; - let request = JSON.parse(spec.buildRequests(validBidRequests, bidderRequest).data); - - assert.equal(request.user.ext.consent, bidderRequest.gdprConsent.consentString); - assert.equal(request.regs.ext.gdpr, bidderRequest.gdprConsent.gdprApplies); - assert.equal(typeof request.regs.ext.gdpr, 'number'); - }); - - it('should send gdpr as number', function () { - let validBidRequests = [{ bidId: 'bidId', params: { test: 1 } }]; - let bidderRequest = { gdprConsent: { gdprApplies: true, consentString: 'consentDataString' }, refererInfo: { page: 'page' } }; + let ortb2 = { + regs: { + ext: { + gdpr: 1 + } + }, + user: { + ext: { + consent: 'consentDataString' + } + } + }; + let bidderRequest = { ortb2, refererInfo: { page: 'page' } }; let request = JSON.parse(spec.buildRequests(validBidRequests, bidderRequest).data); - assert.equal(typeof request.regs.ext.gdpr, 'number'); + assert.equal(request.user.ext.consent, 'consentDataString'); assert.equal(request.regs.ext.gdpr, 1); }); it('should send CCPA Consent data to adform', function () { let validBidRequests = [{ bidId: 'bidId', params: { test: 1 } }]; - let bidderRequest = { uspConsent: '1YA-', refererInfo: { page: 'page' } }; + let ortb2 = { + regs: { + ext: { + us_privacy: '1YA-' + } + } + }; + let bidderRequest = { ortb2, refererInfo: { page: 'page' } }; let request = JSON.parse(spec.buildRequests(validBidRequests, bidderRequest).data); assert.equal(request.regs.ext.us_privacy, '1YA-'); - bidderRequest = { uspConsent: '1YA-', gdprConsent: { gdprApplies: true, consentString: 'consentDataString' }, refererInfo: { page: 'page' } }; + ortb2.regs.ext.gdpr = 1; + ortb2.user = { ext: { consent: 'consentDataString' } }; + + bidderRequest = { ortb2, refererInfo: { page: 'page' } }; request = JSON.parse(spec.buildRequests(validBidRequests, bidderRequest).data); assert.equal(request.regs.ext.us_privacy, '1YA-'); @@ -116,34 +128,6 @@ describe('Adf adapter', function () { assert.equal(request.regs.ext.gdpr, 1); }); - it('should not send GDPR Consent data to adform if gdprApplies is undefined', function () { - let validBidRequests = [{ - bidId: 'bidId', - params: { siteId: 'siteId' } - }]; - let bidderRequest = { gdprConsent: {gdprApplies: false, consentString: 'consentDataString'}, refererInfo: { page: 'page' } }; - let request = JSON.parse(spec.buildRequests(validBidRequests, bidderRequest).data); - - assert.equal(request.user.ext.consent, 'consentDataString'); - assert.equal(request.regs.ext.gdpr, 0); - - bidderRequest = {gdprConsent: {consentString: 'consentDataString'}, refererInfo: { page: 'page' }}; - request = JSON.parse(spec.buildRequests(validBidRequests, bidderRequest).data); - - assert.equal(request.user, undefined); - assert.equal(request.regs, undefined); - }); - it('should send default GDPR Consent data to adform', function () { - let validBidRequests = [{ - bidId: 'bidId', - params: { siteId: 'siteId' } - }]; - let request = JSON.parse(spec.buildRequests(validBidRequests, { refererInfo: { page: 'page' } }).data); - - assert.equal(request.user, undefined); - assert.equal(request.regs, undefined); - }); - it('should transfer DSA info', function () { let validBidRequests = [ { bidId: 'bidId', params: { siteId: 'siteId' } } ]; @@ -200,7 +184,7 @@ describe('Adf adapter', function () { }); it('should have default request structure', function () { - let keys = 'site,device,source,ext,imp'.split(','); + let keys = 'site,user,device,source,ext,imp,regs'.split(','); let validBidRequests = [{ bidId: 'bidId', params: { siteId: 'siteId' } @@ -225,34 +209,30 @@ describe('Adf adapter', function () { assert.equal(request.source.fd, 1); }); - it('should not set coppa when coppa is not provided or is set to false', function () { - config.setConfig({ - }); + it('should send coppa flag', function () { + let ortb2 = { regs: { coppa: 1 } }; let validBidRequests = [{ bidId: 'bidId', params: { test: 1 } }]; - let bidderRequest = { gdprConsent: { gdprApplies: true, consentString: 'consentDataString' }, refererInfo: { page: 'page' } }; - let request = JSON.parse(spec.buildRequests(validBidRequests, bidderRequest).data); - - assert.equal(request.regs.coppa, undefined); + let request = JSON.parse(spec.buildRequests(validBidRequests, { ortb2, refererInfo: { page: 'page' } }).data); - config.setConfig({ - coppa: false - }); - request = JSON.parse(spec.buildRequests(validBidRequests, bidderRequest).data); - - assert.equal(request.regs.coppa, undefined); + assert.equal(request.regs.coppa, 1); }); - it('should set coppa to 1 when coppa is provided with value true', function () { + it('should send info about device', function () { config.setConfig({ - coppa: true + device: { w: 100, h: 100 } }); - let validBidRequests = [{ bidId: 'bidId', params: { test: 1 } }]; + let validBidRequests = [{ + bidId: 'bidId', + params: { mid: '1000' } + }]; let request = JSON.parse(spec.buildRequests(validBidRequests, { refererInfo: { page: 'page' } }).data); - assert.equal(request.regs.coppa, 1); + assert.equal(request.device.ua, navigator.userAgent); + assert.equal(request.device.w, 100); + assert.equal(request.device.h, 100); }); - it('should send info about device', function () { + it('should merge ortb2.device info', function () { config.setConfig({ device: { w: 100, h: 100 } }); @@ -260,11 +240,13 @@ describe('Adf adapter', function () { bidId: 'bidId', params: { mid: '1000' } }]; - let request = JSON.parse(spec.buildRequests(validBidRequests, { refererInfo: { page: 'page' } }).data); + let ortb2 = { device: { ua: 'customUA', w: 200, geo: { lat: 1, lon: 1 } } }; + let request = JSON.parse(spec.buildRequests(validBidRequests, { ortb2, refererInfo: { page: 'page' } }).data); - assert.equal(request.device.ua, navigator.userAgent); - assert.equal(request.device.w, 100); + assert.equal(request.device.ua, 'customUA'); + assert.equal(request.device.w, 200); assert.equal(request.device.h, 100); + assert.deepEqual(request.device.geo, { lat: 1, lon: 1 }); }); it('should send app info', function () { @@ -1075,7 +1057,8 @@ describe('Adf adapter', function () { }], adrender: 1 } - } + }, + cat: [ 'IAB1', 'IAB2' ] } ] }], @@ -1135,6 +1118,8 @@ describe('Adf adapter', function () { assert.deepEqual(bids[0].currency, serverResponse.body.cur); assert.deepEqual(bids[0].mediaType, 'native'); assert.deepEqual(bids[0].meta.mediaType, 'native'); + assert.deepEqual(bids[0].meta.primaryCatId, 'IAB1'); + assert.deepEqual(bids[0].meta.secondaryCatIds, [ 'IAB2' ]); assert.deepEqual(bids[0].meta.advertiserDomains, [ 'demo.com' ]); assert.deepEqual(bids[0].meta.dsa, { behalf: 'some-behalf', From c13b1b8b658b6ba334b4ebc9a24342d457585a60 Mon Sep 17 00:00:00 2001 From: Carlos Felix Date: Wed, 18 Dec 2024 12:54:23 -0600 Subject: [PATCH 123/123] 33across ID System: Include hashed email from storage (#12529) * 33across ID System: Send hm param * fix typo thirthy to thirty * remove all the ID storage keys when response is successful but doesn't contain ID anymore. * Allow to send hashed email via config parameters --- modules/33acrossIdSystem.js | 24 ++- modules/33acrossIdSystem.md | 5 + test/spec/modules/33acrossIdSystem_spec.js | 202 +++++++++++++++------ 3 files changed, 173 insertions(+), 58 deletions(-) diff --git a/modules/33acrossIdSystem.js b/modules/33acrossIdSystem.js index 8f99846017a..fb5a7712f1f 100644 --- a/modules/33acrossIdSystem.js +++ b/modules/33acrossIdSystem.js @@ -27,6 +27,7 @@ const GVLID = 58; const STORAGE_FPID_KEY = '33acrossIdFp'; const STORAGE_TPID_KEY = '33acrossIdTp'; +const STORAGE_HEM_KEY = '33acrossIdHm' const DEFAULT_1PID_SUPPORT = true; const DEFAULT_TPID_SUPPORT = true; @@ -59,7 +60,7 @@ function calculateResponseObj(response) { }; } -function calculateQueryStringParams(pid, gdprConsentData, enabledStorageTypes) { +function calculateQueryStringParams({ pid, hem }, gdprConsentData, enabledStorageTypes) { const uspString = uspDataHandler.getConsentData(); const coppaValue = coppaDataHandler.getCoppa(); const gppConsent = gppDataHandler.getConsentData(); @@ -97,6 +98,11 @@ function calculateQueryStringParams(pid, gdprConsentData, enabledStorageTypes) { params.tp = encodeURIComponent(tp); } + const hemParam = hem || getStoredValue(STORAGE_HEM_KEY, enabledStorageTypes); + if (hemParam) { + params.sha256 = encodeURIComponent(hemParam); + } + return params; } @@ -146,7 +152,7 @@ function handleSupplementalId(key, id, storageConfig) { } /** @type {Submodule} */ -export const thirthyThreeAcrossIdSubmodule = { +export const thirtyThreeAcrossIdSubmodule = { /** * used to link submodule with config * @type {string} @@ -188,7 +194,11 @@ export const thirthyThreeAcrossIdSubmodule = { return; } - const { pid, storeFpid = DEFAULT_1PID_SUPPORT, storeTpid = DEFAULT_TPID_SUPPORT, apiUrl = API_URL } = params; + const { + storeFpid = DEFAULT_1PID_SUPPORT, + storeTpid = DEFAULT_TPID_SUPPORT, apiUrl = API_URL, + ...options + } = params; return { callback(cb) { @@ -203,7 +213,9 @@ export const thirthyThreeAcrossIdSubmodule = { } if (!responseObj.envelope) { - deleteFromStorage(MODULE_NAME); + ['', '_last', '_exp', '_cst'].forEach(suffix => { + deleteFromStorage(`${MODULE_NAME}${suffix}`); + }); } if (storeFpid) { @@ -227,7 +239,7 @@ export const thirthyThreeAcrossIdSubmodule = { cb(); } - }, calculateQueryStringParams(pid, gdprConsentData, enabledStorageTypes), { + }, calculateQueryStringParams(options, gdprConsentData, enabledStorageTypes), { method: 'GET', withCredentials: true }); @@ -246,4 +258,4 @@ export const thirthyThreeAcrossIdSubmodule = { } }; -submodule('userId', thirthyThreeAcrossIdSubmodule); +submodule('userId', thirtyThreeAcrossIdSubmodule); diff --git a/modules/33acrossIdSystem.md b/modules/33acrossIdSystem.md index e983c8ab871..b6b68622344 100644 --- a/modules/33acrossIdSystem.md +++ b/modules/33acrossIdSystem.md @@ -51,5 +51,10 @@ The following settings are available in the `params` property in `userSync.userI | Param name | Scope | Type | Description | Example | | --- | --- | --- | --- | --- | | pid | Required | String | Partner ID provided by 33Across | `"0010b00002GYU4eBAH"` | +| hem | Optional | String | Hashed email address in sha256 format | `"ba4235544d6c91865fbf70fa1bdb70f2d375ded1b2b946b21c675dcbe9968cdc"` | | storeFpid | Optional | Boolean | Indicates whether a supplemental first-party ID may be stored to improve addressability, this feature is enabled by default | `true` (default) or `false` | | storeTpid | Optional | Boolean | Indicates whether a supplemental third-party ID may be stored to improve addressability, this feature is enabled by default | `true` (default) or `false` | + +### HEM Collection + +33Across ID System supports user's hashed email, if available in storage. diff --git a/test/spec/modules/33acrossIdSystem_spec.js b/test/spec/modules/33acrossIdSystem_spec.js index 6ec3554d353..93e6a41d928 100644 --- a/test/spec/modules/33acrossIdSystem_spec.js +++ b/test/spec/modules/33acrossIdSystem_spec.js @@ -1,4 +1,4 @@ -import { thirthyThreeAcrossIdSubmodule, storage, domainUtils } from 'modules/33acrossIdSystem.js'; +import { thirtyThreeAcrossIdSubmodule, storage, domainUtils } from 'modules/33acrossIdSystem.js'; import * as utils from 'src/utils.js'; import { server } from 'test/mocks/xhr.js'; @@ -10,13 +10,13 @@ import {attachIdSystem} from '../../../modules/userId/index.js'; describe('33acrossIdSystem', () => { describe('name', () => { it('should expose the name of the submodule', () => { - expect(thirthyThreeAcrossIdSubmodule.name).to.equal('33acrossId'); + expect(thirtyThreeAcrossIdSubmodule.name).to.equal('33acrossId'); }); }); describe('gvlid', () => { it('should expose the vendor id', () => { - expect(thirthyThreeAcrossIdSubmodule.gvlid).to.equal(58); + expect(thirtyThreeAcrossIdSubmodule.gvlid).to.equal(58); }); }); @@ -24,7 +24,7 @@ describe('33acrossIdSystem', () => { it('should call endpoint and handle valid response', () => { const completeCallback = sinon.spy(); - const { callback } = thirthyThreeAcrossIdSubmodule.getId({ + const { callback } = thirtyThreeAcrossIdSubmodule.getId({ params: { pid: '12345' } @@ -65,7 +65,7 @@ describe('33acrossIdSystem', () => { it('should store the provided first-party ID in a cookie', () => { const completeCallback = () => {}; - const { callback } = thirthyThreeAcrossIdSubmodule.getId({ + const { callback } = thirtyThreeAcrossIdSubmodule.getId({ params: { pid: '12345', ...opts @@ -103,7 +103,7 @@ describe('33acrossIdSystem', () => { it('should store the provided first-party ID in local storage', () => { const completeCallback = () => {}; - const { callback } = thirthyThreeAcrossIdSubmodule.getId({ + const { callback } = thirtyThreeAcrossIdSubmodule.getId({ params: { pid: '12345', ...opts @@ -139,7 +139,7 @@ describe('33acrossIdSystem', () => { it('should store the provided first-party ID in each storage type', () => { const completeCallback = () => {}; - const { callback } = thirthyThreeAcrossIdSubmodule.getId({ + const { callback } = thirtyThreeAcrossIdSubmodule.getId({ params: { pid: '12345', ...opts @@ -181,7 +181,7 @@ describe('33acrossIdSystem', () => { it('should wipe any existing first-party ID from storage', () => { const completeCallback = () => {}; - const { callback } = thirthyThreeAcrossIdSubmodule.getId({ + const { callback } = thirtyThreeAcrossIdSubmodule.getId({ params: { pid: '12345', ...opts @@ -224,7 +224,7 @@ describe('33acrossIdSystem', () => { it('should store the provided third-party ID in a cookie', () => { const completeCallback = () => {}; - const { callback } = thirthyThreeAcrossIdSubmodule.getId({ + const { callback } = thirtyThreeAcrossIdSubmodule.getId({ params: { pid: '12345', ...opts @@ -262,7 +262,7 @@ describe('33acrossIdSystem', () => { it('should store the provided third-party ID in local storage', () => { const completeCallback = () => {}; - const { callback } = thirthyThreeAcrossIdSubmodule.getId({ + const { callback } = thirtyThreeAcrossIdSubmodule.getId({ params: { pid: '12345', ...opts @@ -298,7 +298,7 @@ describe('33acrossIdSystem', () => { it('should store the provided third-party ID in each storage type', () => { const completeCallback = () => {}; - const { callback } = thirthyThreeAcrossIdSubmodule.getId({ + const { callback } = thirtyThreeAcrossIdSubmodule.getId({ params: { pid: '12345', ...opts @@ -340,7 +340,7 @@ describe('33acrossIdSystem', () => { it('should wipe any existing third-party ID from storage', () => { const completeCallback = () => {}; - const { callback } = thirthyThreeAcrossIdSubmodule.getId({ + const { callback } = thirtyThreeAcrossIdSubmodule.getId({ params: { pid: '12345', ...opts @@ -383,7 +383,7 @@ describe('33acrossIdSystem', () => { it('should not store the provided first-party ID in a cookie', () => { const completeCallback = () => {}; - const { callback } = thirthyThreeAcrossIdSubmodule.getId({ + const { callback } = thirtyThreeAcrossIdSubmodule.getId({ params: { pid: '12345', storeFpid: false @@ -421,7 +421,7 @@ describe('33acrossIdSystem', () => { it('should not store the provided first-party ID in local storage', () => { const completeCallback = () => {}; - const { callback } = thirthyThreeAcrossIdSubmodule.getId({ + const { callback } = thirtyThreeAcrossIdSubmodule.getId({ params: { pid: '12345', storeFpid: false @@ -459,7 +459,7 @@ describe('33acrossIdSystem', () => { it('should not store the provided third-party ID in a cookie', () => { const completeCallback = () => {}; - const { callback } = thirthyThreeAcrossIdSubmodule.getId({ + const { callback } = thirtyThreeAcrossIdSubmodule.getId({ params: { pid: '12345', storeTpid: false @@ -495,7 +495,7 @@ describe('33acrossIdSystem', () => { it('should not store the provided third-party ID in local storage', () => { const completeCallback = () => {}; - const { callback } = thirthyThreeAcrossIdSubmodule.getId({ + const { callback } = thirtyThreeAcrossIdSubmodule.getId({ params: { pid: '12345', storeTpid: false @@ -532,7 +532,7 @@ describe('33acrossIdSystem', () => { it('should wipe any existing "envelope" ID from storage', () => { const completeCallback = () => {}; - const { callback } = thirthyThreeAcrossIdSubmodule.getId({ + const { callback } = thirtyThreeAcrossIdSubmodule.getId({ params: { pid: '12345' }, @@ -558,8 +558,10 @@ describe('33acrossIdSystem', () => { expires: 1645667805067 })); - expect(removeDataFromLocalStorage.calledWith('33acrossId')).to.be.true; - expect(setCookie.calledWithExactly('33acrossId', '', sinon.match.string, 'Lax', 'foo.com')).to.be.true; + ['', '_last', '_exp', '_cst'].forEach(suffix => { + expect(removeDataFromLocalStorage.calledWith(`33acrossId${suffix}`)).to.be.true; + expect(setCookie.calledWithExactly(`33acrossId${suffix}`, '', sinon.match.string, 'Lax', 'foo.com')).to.be.true; + }); removeDataFromLocalStorage.restore(); setCookie.restore(); @@ -571,7 +573,7 @@ describe('33acrossIdSystem', () => { it('should log a warning and don\'t expect a call to the endpoint', () => { const logWarnSpy = sinon.spy(utils, 'logWarn'); - const result = thirthyThreeAcrossIdSubmodule.getId({ + const result = thirtyThreeAcrossIdSubmodule.getId({ params: { pid: '12345' } @@ -589,7 +591,7 @@ describe('33acrossIdSystem', () => { context('when GDPR doesn\'t apply', () => { it('should call endpoint with \'gdpr=0\'', () => { const completeCallback = () => {}; - const { callback } = thirthyThreeAcrossIdSubmodule.getId({ + const { callback } = thirtyThreeAcrossIdSubmodule.getId({ params: { pid: '12345' } @@ -607,7 +609,7 @@ describe('33acrossIdSystem', () => { context('but the GDPR consent string is given', () => { it('should call endpoint with the GDPR consent string', () => { const completeCallback = () => {}; - const { callback } = thirthyThreeAcrossIdSubmodule.getId({ + const { callback } = thirtyThreeAcrossIdSubmodule.getId({ params: { pid: '12345' } @@ -628,7 +630,7 @@ describe('33acrossIdSystem', () => { context('when a valid US Privacy string is given', () => { it('should call endpoint with the US Privacy parameter', () => { const completeCallback = () => {}; - const { callback } = thirthyThreeAcrossIdSubmodule.getId({ + const { callback } = thirtyThreeAcrossIdSubmodule.getId({ params: { pid: '12345' } @@ -649,7 +651,7 @@ describe('33acrossIdSystem', () => { context('when an invalid US Privacy is given', () => { it('should call endpoint without the US Privacy parameter', () => { const completeCallback = () => {}; - const { callback } = thirthyThreeAcrossIdSubmodule.getId({ + const { callback } = thirtyThreeAcrossIdSubmodule.getId({ params: { pid: '12345' } @@ -671,7 +673,7 @@ describe('33acrossIdSystem', () => { context('when coppa is enabled', () => { it('should call endpoint with an enabled coppa signal', () => { const completeCallback = () => {}; - const { callback } = thirthyThreeAcrossIdSubmodule.getId({ + const { callback } = thirtyThreeAcrossIdSubmodule.getId({ params: { pid: '12345' } @@ -692,7 +694,7 @@ describe('33acrossIdSystem', () => { context('when coppa is not enabled', () => { it('should call endpoint with coppa signal not enabled', () => { const completeCallback = () => {}; - const { callback } = thirthyThreeAcrossIdSubmodule.getId({ + const { callback } = thirtyThreeAcrossIdSubmodule.getId({ params: { pid: '12345' } @@ -726,7 +728,7 @@ describe('33acrossIdSystem', () => { { gppString: 'foo', expected: 'foo' }, ].forEach(({ gppString, expected }, index) => { const completeCallback = () => {}; - const { callback } = thirthyThreeAcrossIdSubmodule.getId({ + const { callback } = thirtyThreeAcrossIdSubmodule.getId({ params: { pid: '12345' } @@ -752,7 +754,7 @@ describe('33acrossIdSystem', () => { { applicableSections: ['1', '2'], expected: '1%2C2' }, ].forEach(({ applicableSections, expected }, index) => { const completeCallback = () => {}; - const { callback } = thirthyThreeAcrossIdSubmodule.getId({ + const { callback } = thirtyThreeAcrossIdSubmodule.getId({ params: { pid: '12345' } @@ -773,7 +775,7 @@ describe('33acrossIdSystem', () => { context('when a first-party ID is present in local storage', () => { it('should call endpoint with the encoded first-party ID included', () => { const completeCallback = () => {}; - const { callback } = thirthyThreeAcrossIdSubmodule.getId({ + const { callback } = thirtyThreeAcrossIdSubmodule.getId({ params: { pid: '12345' }, @@ -798,7 +800,7 @@ describe('33acrossIdSystem', () => { context('when a first-party ID is present in cookie storage', () => { it('should call endpoint with the first-party ID included', () => { const completeCallback = () => {}; - const { callback } = thirthyThreeAcrossIdSubmodule.getId({ + const { callback } = thirtyThreeAcrossIdSubmodule.getId({ params: { pid: '12345' }, @@ -823,7 +825,7 @@ describe('33acrossIdSystem', () => { context('when a first-party ID is not present in storage', () => { it('should not call endpoint with the first-party ID included', () => { const completeCallback = () => {}; - const { callback } = thirthyThreeAcrossIdSubmodule.getId({ + const { callback } = thirtyThreeAcrossIdSubmodule.getId({ params: { pid: '12345' } @@ -840,7 +842,7 @@ describe('33acrossIdSystem', () => { context('when a third-party ID is present in local storage', () => { it('should call endpoint with the encoded third-party ID included', () => { const completeCallback = () => {}; - const { callback } = thirthyThreeAcrossIdSubmodule.getId({ + const { callback } = thirtyThreeAcrossIdSubmodule.getId({ params: { pid: '12345' }, @@ -865,7 +867,7 @@ describe('33acrossIdSystem', () => { context('when a third-party ID is present in cookie storage', () => { it('should call endpoint with the third-party ID included', () => { const completeCallback = () => {}; - const { callback } = thirthyThreeAcrossIdSubmodule.getId({ + const { callback } = thirtyThreeAcrossIdSubmodule.getId({ params: { pid: '12345' }, @@ -890,7 +892,7 @@ describe('33acrossIdSystem', () => { context('when a third-party ID is not present in storage', () => { it('should not call endpoint with the third-party ID included', () => { const completeCallback = () => {}; - const { callback } = thirthyThreeAcrossIdSubmodule.getId({ + const { callback } = thirtyThreeAcrossIdSubmodule.getId({ params: { pid: '12345' } @@ -904,11 +906,107 @@ describe('33acrossIdSystem', () => { }); }); + context('when a hashed email is provided via configuration options', () => { + it('should call endpoint with the hashed email included', () => { + const completeCallback = () => {}; + const { callback } = thirtyThreeAcrossIdSubmodule.getId({ + params: { + pid: '12345', + hem: '33acrossIdHmValue+' + }, + enabledStorageTypes: [ 'html5' ], + storage: {} + }); + + // Prioritizes the hem given via config options over the one stored in LS. + sinon.stub(storage, 'getDataFromLocalStorage') + .withArgs('33acrossIdHm') + .returns('33acrossIdHmValueLS'); + + callback(completeCallback); + + const [request] = server.requests; + + expect(request.url).to.contain('sha256=33acrossIdHmValue%2B'); + + storage.getDataFromLocalStorage.restore(); + }); + }); + + context('when a hashed email is NOT provided via configuration options', () => { + context('but it\'s provided via local storage', () => { + it('should call endpoint with the hashed email included', () => { + const completeCallback = () => {}; + const { callback } = thirtyThreeAcrossIdSubmodule.getId({ + params: { + pid: '12345' + }, + enabledStorageTypes: [ 'html5' ], + storage: {} + }); + + sinon.stub(storage, 'getDataFromLocalStorage') + .withArgs('33acrossIdHm') + .returns('33acrossIdHmValue+'); + + callback(completeCallback); + + const [request] = server.requests; + + expect(request.url).to.contain('sha256=33acrossIdHmValue%2B'); + + storage.getDataFromLocalStorage.restore(); + }); + }); + + context('but it\'s provided via cookie storage', () => { + it('should call endpoint with the hashed email included', () => { + const completeCallback = () => {}; + const { callback } = thirtyThreeAcrossIdSubmodule.getId({ + params: { + pid: '12345' + }, + enabledStorageTypes: [ 'cookie' ], + storage: {} + }); + + sinon.stub(storage, 'getCookie') + .withArgs('33acrossIdHm') + .returns('33acrossIdHmValue'); + + callback(completeCallback); + + const [request] = server.requests; + + expect(request.url).to.contain('sha256=33acrossIdHmValue'); + + storage.getCookie.restore(); + }); + }); + + context('and hashed email is not present in storage', () => { + it('should not call endpoint with the hashed email included', () => { + const completeCallback = () => {}; + const { callback } = thirtyThreeAcrossIdSubmodule.getId({ + params: { + pid: '12345' + } + }); + + callback(completeCallback); + + const [request] = server.requests; + + expect(request.url).not.to.contain('sha256='); + }); + }); + }); + context('when the partner ID is not given', () => { it('should log an error', () => { const logErrorSpy = sinon.spy(utils, 'logError'); - thirthyThreeAcrossIdSubmodule.getId({ + thirtyThreeAcrossIdSubmodule.getId({ params: { /* No 'pid' param */ } }); @@ -922,7 +1020,7 @@ describe('33acrossIdSystem', () => { it('should log an error', () => { const logErrorSpy = sinon.spy(utils, 'logError'); - thirthyThreeAcrossIdSubmodule.getId({ + thirtyThreeAcrossIdSubmodule.getId({ params: { pid: 123456 // PID must be a string } @@ -939,7 +1037,7 @@ describe('33acrossIdSystem', () => { const logErrorSpy = sinon.spy(utils, 'logError'); const completeCallback = () => {}; - const { callback } = thirthyThreeAcrossIdSubmodule.getId({ + const { callback } = thirtyThreeAcrossIdSubmodule.getId({ params: { pid: '12345' } @@ -953,7 +1051,7 @@ describe('33acrossIdSystem', () => { 'Content-Type': 'application/json' }, 'invalid response'); - expect(logErrorSpy.lastCall.args[0]).to.eq(`${thirthyThreeAcrossIdSubmodule.name}: ID reading error:`); + expect(logErrorSpy.lastCall.args[0]).to.eq(`${thirtyThreeAcrossIdSubmodule.name}: ID reading error:`); logErrorSpy.restore(); }); @@ -961,7 +1059,7 @@ describe('33acrossIdSystem', () => { it('should execute complete callback with undefined value', () => { const completeCallback = sinon.spy(); - const { callback } = thirthyThreeAcrossIdSubmodule.getId({ + const { callback } = thirtyThreeAcrossIdSubmodule.getId({ params: { pid: '12345' } @@ -982,7 +1080,7 @@ describe('33acrossIdSystem', () => { context('when an endpoint override is given', () => { it('should call that endpoint', () => { const completeCallback = sinon.spy(); - const { callback } = thirthyThreeAcrossIdSubmodule.getId({ + const { callback } = thirtyThreeAcrossIdSubmodule.getId({ params: { pid: '12345', apiUrl: 'https://staging-lexicon.33across.com/v1/envelope' @@ -1012,7 +1110,7 @@ describe('33acrossIdSystem', () => { const logErrorSpy = sinon.spy(utils, 'logError'); const completeCallback = () => {}; - const { callback } = thirthyThreeAcrossIdSubmodule.getId({ + const { callback } = thirtyThreeAcrossIdSubmodule.getId({ params: { pid: '12345' } @@ -1029,7 +1127,7 @@ describe('33acrossIdSystem', () => { error: 'foo' })); - expect(logErrorSpy.calledOnceWithExactly(`${thirthyThreeAcrossIdSubmodule.name}: Unsuccessful response foo`)).to.be.true; + expect(logErrorSpy.calledOnceWithExactly(`${thirtyThreeAcrossIdSubmodule.name}: Unsuccessful response foo`)).to.be.true; logErrorSpy.restore(); }); @@ -1037,7 +1135,7 @@ describe('33acrossIdSystem', () => { it('should execute complete callback with undefined value', () => { const completeCallback = sinon.spy(); - const { callback } = thirthyThreeAcrossIdSubmodule.getId({ + const { callback } = thirtyThreeAcrossIdSubmodule.getId({ params: { pid: '12345' } @@ -1063,7 +1161,7 @@ describe('33acrossIdSystem', () => { const logMessageSpy = sinon.spy(utils, 'logMessage'); const completeCallback = () => {}; - const { callback } = thirthyThreeAcrossIdSubmodule.getId({ + const { callback } = thirtyThreeAcrossIdSubmodule.getId({ params: { pid: '12345' } @@ -1080,7 +1178,7 @@ describe('33acrossIdSystem', () => { data: {} })); - expect(logMessageSpy.calledOnceWithExactly(`${thirthyThreeAcrossIdSubmodule.name}: No envelope was received`)).to.be.true; + expect(logMessageSpy.calledOnceWithExactly(`${thirtyThreeAcrossIdSubmodule.name}: No envelope was received`)).to.be.true; logMessageSpy.restore(); }); @@ -1088,7 +1186,7 @@ describe('33acrossIdSystem', () => { it('should execute complete callback with undefined value', () => { const completeCallback = sinon.spy(); - const { callback } = thirthyThreeAcrossIdSubmodule.getId({ + const { callback } = thirtyThreeAcrossIdSubmodule.getId({ params: { pid: '12345' } @@ -1114,7 +1212,7 @@ describe('33acrossIdSystem', () => { const logErrorSpy = sinon.spy(utils, 'logError'); const completeCallback = () => {}; - const { callback } = thirthyThreeAcrossIdSubmodule.getId({ + const { callback } = thirtyThreeAcrossIdSubmodule.getId({ params: { pid: '12345' } @@ -1126,7 +1224,7 @@ describe('33acrossIdSystem', () => { request.respond(404); - expect(logErrorSpy.calledOnceWithExactly(`${thirthyThreeAcrossIdSubmodule.name}: ID error response`, 'Not Found')).to.be.true; + expect(logErrorSpy.calledOnceWithExactly(`${thirtyThreeAcrossIdSubmodule.name}: ID error response`, 'Not Found')).to.be.true; logErrorSpy.restore(); }); @@ -1134,7 +1232,7 @@ describe('33acrossIdSystem', () => { it('should execute complete callback without any value', () => { const completeCallback = sinon.spy(); - const { callback } = thirthyThreeAcrossIdSubmodule.getId({ + const { callback } = thirtyThreeAcrossIdSubmodule.getId({ params: { pid: '12345' } @@ -1153,8 +1251,8 @@ describe('33acrossIdSystem', () => { describe('decode', () => { it('should wrap the given value inside an object literal', () => { - expect(thirthyThreeAcrossIdSubmodule.decode('foo')).to.deep.equal({ - [thirthyThreeAcrossIdSubmodule.name]: { + expect(thirtyThreeAcrossIdSubmodule.decode('foo')).to.deep.equal({ + [thirtyThreeAcrossIdSubmodule.name]: { envelope: 'foo' } }); @@ -1162,7 +1260,7 @@ describe('33acrossIdSystem', () => { }); describe('eid', () => { before(() => { - attachIdSystem(thirthyThreeAcrossIdSubmodule); + attachIdSystem(thirtyThreeAcrossIdSubmodule); }) it('33acrossId', function() { const userId = {