diff --git a/modules/yandexBidAdapter.js b/modules/yandexBidAdapter.js index cb1cbd89c84..e694dbcfe03 100644 --- a/modules/yandexBidAdapter.js +++ b/modules/yandexBidAdapter.js @@ -1,8 +1,8 @@ -import { formatQS, deepAccess, deepSetValue, triggerPixel, _each, _map } from '../src/utils.js'; import { registerBidder } from '../src/adapters/bidderFactory.js'; -import { BANNER, NATIVE } from '../src/mediaTypes.js' -import { convertOrtbRequestToProprietaryNative } from '../src/native.js'; import { config } from '../src/config.js'; +import { BANNER, NATIVE } from '../src/mediaTypes.js'; +import { convertOrtbRequestToProprietaryNative } from '../src/native.js'; +import { _each, _map, deepAccess, deepSetValue, formatQS, triggerPixel } from '../src/utils.js'; /** * @typedef {import('../src/adapters/bidderFactory.js').Bid} Bid @@ -53,7 +53,7 @@ const DEFAULT_CURRENCY = 'EUR'; /** * @type {MediaType[]} */ -const SUPPORTED_MEDIA_TYPES = [ BANNER, NATIVE ]; +const SUPPORTED_MEDIA_TYPES = [BANNER, NATIVE]; const SSP_ID = 10500; const IMAGE_ASSET_TYPES = { @@ -121,15 +121,7 @@ export const spec = { buildRequests: function(validBidRequests, bidderRequest) { validBidRequests = convertOrtbRequestToProprietaryNative(validBidRequests); - let referrer = ''; - let domain = ''; - let page = ''; - - if (bidderRequest && bidderRequest.refererInfo) { - referrer = bidderRequest.refererInfo.ref; - domain = bidderRequest.refererInfo.domain; - page = bidderRequest.refererInfo.page; - } + const ortb2 = bidderRequest.ortb2; let timeout = null; if (bidderRequest) { @@ -146,7 +138,7 @@ export const spec = { const queryParams = { 'imp-id': impId, - 'target-ref': targetRef || domain, + 'target-ref': targetRef || ortb2?.site?.domain, 'ssp-id': SSP_ID, }; @@ -177,12 +169,10 @@ export const spec = { const data = { id: bidRequest.bidId, imp: [imp], - site: { - ref: referrer, - page, - domain, - }, + site: ortb2?.site, tmax: timeout, + user: ortb2?.user, + device: ortb2?.device, }; const eids = deepAccess(bidRequest, 'userIdAsEids'); @@ -190,11 +180,6 @@ export const spec = { deepSetValue(data, 'user.ext.eids', eids); } - const userData = deepAccess(bidRequest, 'ortb2.user.data'); - if (userData && userData.length) { - deepSetValue(data, 'user.data', userData); - } - const queryParamsString = formatQS(queryParams); return { method: 'POST', @@ -274,8 +259,8 @@ function getBidfloor(bidRequest) { const floorInfo = bidRequest.getFloor({ currency: DEFAULT_CURRENCY, mediaType: type, - size: bidRequest.sizes || '*' } - ) + size: bidRequest.sizes || '*' + }) floors.push(floorInfo); } }); @@ -332,7 +317,7 @@ function mapNative(bidRequest) { } function mapAsset(assetCode, adUnitAssetParams, nativeAsset) { - const [ nativeAssetId, nativeAssetType ] = nativeAsset; + const [nativeAssetId, nativeAssetType] = nativeAsset; const asset = { id: nativeAssetId, }; diff --git a/src/auction.js b/src/auction.js index cc42e54a2b1..5e2590e89aa 100644 --- a/src/auction.js +++ b/src/auction.js @@ -45,6 +45,7 @@ * @property {refererInfo} refererInfo - referer info object * @property {string} [tid] - random UUID (used for s2s) * @property {string} [src] - s2s or client (used for s2s) + * @property {import('./types/ortb2.js').Ortb2.BidRequest} [ortb2] Global (not specific to any adUnit) first party data to use for all requests in this auction. */ /** diff --git a/src/fpd/enrichment.js b/src/fpd/enrichment.js index 911509455e0..49e2f7d7cad 100644 --- a/src/fpd/enrichment.js +++ b/src/fpd/enrichment.js @@ -114,13 +114,19 @@ const ENRICHMENTS = { const w = win.innerWidth || win.document.documentElement.clientWidth || win.document.body.clientWidth; const h = win.innerHeight || win.document.documentElement.clientHeight || win.document.body.clientHeight; - return { + const device = { w, h, dnt: getDNT() ? 1 : 0, ua: win.navigator.userAgent, language: win.navigator.language.split('-').shift(), }; + + if (win.navigator?.webdriver) { + deepSetValue(device, 'ext.webdriver', true); + } + + return device; }) }, regs() { diff --git a/src/types/ortb2.d.ts b/src/types/ortb2.d.ts new file mode 100644 index 00000000000..f38545c0c31 --- /dev/null +++ b/src/types/ortb2.d.ts @@ -0,0 +1,59 @@ +/** + * @see https://iabtechlab.com/standards/openrtb/ + */ +export namespace Ortb2 { + type Site = { + page?: string; + ref?: string; + domain?: string; + publisher?: { + domain?: string; + }; + keywords?: string; + ext?: Record; + }; + + type Device = { + w?: number; + h?: number; + dnt?: 0 | 1; + ua?: string; + language?: string; + sua?: { + source?: number; + platform?: unknown; + browsers?: unknown[]; + mobile?: number; + }; + ext?: { + webdriver?: true; + [key: string]: unknown; + }; + }; + + type Regs = { + coppa?: unknown; + ext?: { + gdpr?: unknown; + us_privacy?: unknown; + [key: string]: unknown; + }; + }; + + type User = { + ext?: Record; + }; + + /** + * Ortb2 info provided in bidder request. Some of the sections are mutually exclusive. + * @see clientSectionChecker + */ + type BidRequest = { + device?: Device; + regs?: Regs; + user?: User; + site?: Site; + app?: unknown; + dooh?: unknown; + }; +} diff --git a/test/spec/fpd/enrichment_spec.js b/test/spec/fpd/enrichment_spec.js index 40692360dca..80ee0dd6cd2 100644 --- a/test/spec/fpd/enrichment_spec.js +++ b/test/spec/fpd/enrichment_spec.js @@ -186,6 +186,21 @@ describe('FPD enrichment', () => { }); }); + describe('ext.webdriver', () => { + it('when navigator.webdriver is available', () => { + win.navigator.webdriver = true; + return fpd().then(ortb2 => { + expect(ortb2.device.ext?.webdriver).to.eql(true); + }); + }); + + it('when navigator.webdriver is not present', () => { + return fpd().then(ortb2 => { + expect(ortb2.device.ext?.webdriver).to.not.exist; + }); + }); + }); + it('sets ua', () => { win.navigator.userAgent = 'mock-ua'; return fpd().then(ortb2 => { @@ -362,7 +377,7 @@ describe('FPD enrichment', () => { setup(); cdep = Promise.resolve('example-test-label'); return fpd().then(ortb2 => { - expect(ortb2.device.ext).to.not.exist; + expect(ortb2.device.ext?.cdep).to.not.exist; if (navigator.cookieDeprecationLabel) { sinon.assert.notCalled(navigator.cookieDeprecationLabel.getValue); } @@ -373,7 +388,7 @@ describe('FPD enrichment', () => { it('if the navigator API returns a promise that rejects, the enrichment does not halt forever', () => { cdep = Promise.reject(new Error('oops, something went wrong')); return fpd().then(ortb2 => { - expect(ortb2.device.ext).to.not.exist; + expect(ortb2.device.ext?.cdep).to.not.exist; }) }); }); diff --git a/test/spec/modules/yandexBidAdapter_spec.js b/test/spec/modules/yandexBidAdapter_spec.js index c5f088a2306..140be4121ec 100644 --- a/test/spec/modules/yandexBidAdapter_spec.js +++ b/test/spec/modules/yandexBidAdapter_spec.js @@ -1,8 +1,8 @@ import { assert, expect } from 'chai'; -import { spec, NATIVE_ASSETS } from 'modules/yandexBidAdapter.js'; +import { NATIVE_ASSETS, spec } from 'modules/yandexBidAdapter.js'; import * as utils from 'src/utils.js'; -import { BANNER, NATIVE } from '../../../src/mediaTypes'; import { config } from '../../../src/config'; +import { BANNER, NATIVE } from '../../../src/mediaTypes'; describe('Yandex adapter', function () { describe('isBidRequestValid', function () { @@ -41,11 +41,45 @@ describe('Yandex adapter', function () { }); describe('buildRequests', function () { + /** @type {import('../../../src/auction').BidderRequest} */ const bidderRequest = { - refererInfo: { - domain: 'ya.ru', - ref: 'https://ya.ru/', - page: 'https://ya.ru/', + ortb2: { + site: { + domain: 'ya.ru', + ref: 'https://ya.ru/', + page: 'https://ya.ru/', + publisher: { + domain: 'ya.ru', + }, + }, + device: { + w: 1600, + h: 900, + dnt: 0, + ua: 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36', + language: 'en', + sua: { + source: 1, + platform: { + brand: 'macOS', + }, + browsers: [ + { + brand: 'Not_A Brand', + version: ['8'], + }, + { + brand: 'Chromium', + version: ['120'], + }, + { + brand: 'Google Chrome', + version: ['120'], + }, + ], + mobile: 0, + }, + }, }, gdprConsent: { gdprApplies: 1, @@ -107,12 +141,10 @@ describe('Yandex adapter', function () { }); it('should send eids and ortb2 user data if defined', function() { - const bidRequestExtra = { - userIdAsEids: [{ - source: 'sharedid.org', - uids: [{ id: '01', atype: 1 }], - }], + const bidderRequestWithUserData = { + ...bidderRequest, ortb2: { + ...bidderRequest.ortb2, user: { data: [ { @@ -127,17 +159,24 @@ describe('Yandex adapter', function () { }, ], }, - }, + } + }; + const bidRequestExtra = { + userIdAsEids: [{ + source: 'sharedid.org', + uids: [{ id: '01', atype: 1 }], + }], }; + const expected = { ext: { eids: bidRequestExtra.userIdAsEids, }, - data: bidRequestExtra.ortb2.user.data, + data: bidderRequestWithUserData.ortb2.user.data, }; const bannerRequest = getBidRequest(bidRequestExtra); - const requests = spec.buildRequests([bannerRequest], bidderRequest); + const requests = spec.buildRequests([bannerRequest], bidderRequestWithUserData); expect(requests).to.have.lengthOf(1); const request = requests[0]; @@ -149,6 +188,16 @@ describe('Yandex adapter', function () { expect(data.user).to.deep.equal(expected); }); + it('should send site', function() { + const expected = { + site: bidderRequest.ortb2.site + }; + + const requests = spec.buildRequests([getBidRequest()], bidderRequest); + + expect(requests[0].data.site).to.deep.equal(expected.site); + }); + describe('banner', () => { it('should create valid banner object', () => { const bannerRequest = getBidRequest({ @@ -496,41 +545,46 @@ describe('Yandex adapter', function () { }); it('Should not trigger pixel if bid does not contain nurl', function() { - const result = spec.onBidWon({}); + spec.onBidWon({}); + expect(utils.triggerPixel.callCount).to.equal(0) }) it('Should trigger pixel if bid has nurl', function() { - const result = spec.onBidWon({ + spec.onBidWon({ nurl: 'https://example.com/some-tracker', timeToRespond: 378, }); + expect(utils.triggerPixel.callCount).to.equal(1) expect(utils.triggerPixel.getCall(0).args[0]).to.equal('https://example.com/some-tracker?rtt=378') }) it('Should trigger pixel if bid has nurl with path & params', function() { - const result = spec.onBidWon({ + spec.onBidWon({ nurl: 'https://example.com/some-tracker/abcdxyz?param1=1¶m2=2', timeToRespond: 378, }); + expect(utils.triggerPixel.callCount).to.equal(1) expect(utils.triggerPixel.getCall(0).args[0]).to.equal('https://example.com/some-tracker/abcdxyz?param1=1¶m2=2&rtt=378') }) it('Should trigger pixel if bid has nurl with path & params and rtt macros', function() { - const result = spec.onBidWon({ + spec.onBidWon({ nurl: 'https://example.com/some-tracker/abcdxyz?param1=1¶m2=2&custom-rtt=${RTT}', timeToRespond: 378, }); + expect(utils.triggerPixel.callCount).to.equal(1) expect(utils.triggerPixel.getCall(0).args[0]).to.equal('https://example.com/some-tracker/abcdxyz?param1=1¶m2=2&custom-rtt=378') }) it('Should trigger pixel if bid has nurl and there is no timeToRespond param, but has rtt macros in nurl', function() { - const result = spec.onBidWon({ + spec.onBidWon({ nurl: 'https://example.com/some-tracker/abcdxyz?param1=1¶m2=2&custom-rtt=${RTT}', }); + expect(utils.triggerPixel.callCount).to.equal(1) expect(utils.triggerPixel.getCall(0).args[0]).to.equal('https://example.com/some-tracker/abcdxyz?param1=1¶m2=2&custom-rtt=-1') })