From 52f481338df82e78106dd5b68848be7507983c3c Mon Sep 17 00:00:00 2001 From: Olivier Date: Thu, 25 Feb 2021 16:32:27 +0100 Subject: [PATCH] Native: add custom data assets capability (#6220) Use `mediaTypes.native.ext: {}` special object to add/allow custom data. assets. --- src/native.js | 78 +++++++++++++++++++++++++++------------- test/spec/native_spec.js | 74 ++++++++++++++++++++++++++++++++------ 2 files changed, 117 insertions(+), 35 deletions(-) diff --git a/src/native.js b/src/native.js index 2d22e16c8a5..b43e582327b 100644 --- a/src/native.js +++ b/src/native.js @@ -165,30 +165,38 @@ export function getNativeTargeting(bid, bidReq) { `nativeParams.sendTargetingKeys` ) !== false; - Object.keys(bid['native']).forEach(asset => { - if (asset !== 'adTemplate') { - const key = CONSTANTS.NATIVE_KEYS[asset]; - let value = getAssetValue(bid['native'][asset]); - - const sendPlaceholder = deepAccess( - bidReq, - `mediaTypes.native.${asset}.sendId` - ); - - if (sendPlaceholder) { - const placeholder = `${key}:${bid.adId}`; - value = placeholder; - } - - const assetSendTargetingKeys = deepAccess( - bidReq, - `nativeParams.${asset}.sendTargetingKeys`); - - const sendTargeting = typeof assetSendTargetingKeys === 'boolean' ? assetSendTargetingKeys : globalSendTargetingKeys; - - if (key && value && sendTargeting) { - keyValues[key] = value; - } + const nativeKeys = getNativeKeys(bidReq); + + const flatBidNativeKeys = { ...bid.native, ...bid.native.ext }; + delete flatBidNativeKeys.ext; + + Object.keys(flatBidNativeKeys).forEach(asset => { + const key = nativeKeys[asset]; + let value = getAssetValue(bid.native[asset]) || getAssetValue(deepAccess(bid, `native.ext.${asset}`)); + + if (asset === 'adTemplate' || !key || !value) { + return; + } + + let sendPlaceholder = deepAccess(bidReq, `nativeParams.${asset}.sendId`); + if (typeof sendPlaceholder !== 'boolean') { + sendPlaceholder = deepAccess(bidReq, `nativeParams.ext.${asset}.sendId`); + } + + if (sendPlaceholder) { + const placeholder = `${key}:${bid.adId}`; + value = placeholder; + } + + let assetSendTargetingKeys = deepAccess(bidReq, `nativeParams.${asset}.sendTargetingKeys`) + if (typeof assetSendTargetingKeys !== 'boolean') { + assetSendTargetingKeys = deepAccess(bidReq, `nativeParams.ext.${asset}.sendTargetingKeys`); + } + + const sendTargeting = typeof assetSendTargetingKeys === 'boolean' ? assetSendTargetingKeys : globalSendTargetingKeys; + + if (sendTargeting) { + keyValues[key] = value; } }); @@ -234,6 +242,13 @@ export function getAllAssetsMessage(data, adObject) { message.adTemplate = getAssetValue(adObject.native[key]); } else if (key === 'rendererUrl' && adObject.native[key]) { message.rendererUrl = getAssetValue(adObject.native[key]); + } else if (key === 'ext') { + Object.keys(adObject.native[key]).forEach(extKey => { + if (adObject.native[key][extKey]) { + const value = getAssetValue(adObject.native[key][extKey]); + message.assets.push({ key: extKey, value }); + } + }) } else if (adObject.native[key] && CONSTANTS.NATIVE_KEYS.hasOwnProperty(key)) { const value = getAssetValue(adObject.native[key]); @@ -255,3 +270,18 @@ function getAssetValue(value) { return value; } + +function getNativeKeys(bidReq) { + const extraNativeKeys = {} + + if (deepAccess(bidReq, 'nativeParams.ext')) { + Object.keys(bidReq.nativeParams.ext).forEach(extKey => { + extraNativeKeys[extKey] = `hb_native_${extKey}`; + }) + } + + return { + ...CONSTANTS.NATIVE_KEYS, + ...extraNativeKeys + } +} diff --git a/test/spec/native_spec.js b/test/spec/native_spec.js index ef9d407dd0c..7154a0fde37 100644 --- a/test/spec/native_spec.js +++ b/test/spec/native_spec.js @@ -23,7 +23,11 @@ const bid = { clickUrl: 'https://www.link.example', clickTrackers: ['https://tracker.example'], impressionTrackers: ['https://impression.example'], - javascriptTrackers: '' + javascriptTrackers: '', + ext: { + foo: 'foo-value', + baz: 'baz-value' + } } }; @@ -36,7 +40,11 @@ const bidWithUndefinedFields = { clickUrl: 'https://www.link.example', clickTrackers: ['https://tracker.example'], impressionTrackers: ['https://impression.example'], - javascriptTrackers: '' + javascriptTrackers: '', + ext: { + foo: 'foo-value', + baz: undefined + } } }; @@ -59,14 +67,21 @@ describe('native.js', function () { expect(targeting[CONSTANTS.NATIVE_KEYS.title]).to.equal(bid.native.title); expect(targeting[CONSTANTS.NATIVE_KEYS.body]).to.equal(bid.native.body); expect(targeting[CONSTANTS.NATIVE_KEYS.clickUrl]).to.equal(bid.native.clickUrl); + expect(targeting.hb_native_foo).to.equal(bid.native.foo); }); it('sends placeholders for configured assets', function () { const bidRequest = { - mediaTypes: { - native: { - body: { sendId: true }, - clickUrl: { sendId: true }, + nativeParams: { + body: { sendId: true }, + clickUrl: { sendId: true }, + ext: { + foo: { + sendId: false + }, + baz: { + sendId: true + } } } }; @@ -75,15 +90,33 @@ describe('native.js', function () { expect(targeting[CONSTANTS.NATIVE_KEYS.title]).to.equal(bid.native.title); expect(targeting[CONSTANTS.NATIVE_KEYS.body]).to.equal('hb_native_body:123'); expect(targeting[CONSTANTS.NATIVE_KEYS.clickUrl]).to.equal('hb_native_linkurl:123'); + expect(targeting.hb_native_foo).to.equal(bid.native.ext.foo); + expect(targeting.hb_native_baz).to.equal('hb_native_baz:123'); }); it('should only include native targeting keys with values', function () { - const targeting = getNativeTargeting(bidWithUndefinedFields); + const bidRequest = { + nativeParams: { + body: { sendId: true }, + clickUrl: { sendId: true }, + ext: { + foo: { + required: false + }, + baz: { + required: false + } + } + } + }; + + const targeting = getNativeTargeting(bidWithUndefinedFields, bidRequest); expect(Object.keys(targeting)).to.deep.equal([ CONSTANTS.NATIVE_KEYS.title, CONSTANTS.NATIVE_KEYS.sponsoredBy, - CONSTANTS.NATIVE_KEYS.clickUrl + CONSTANTS.NATIVE_KEYS.clickUrl, + 'hb_native_foo' ]); }); @@ -138,6 +171,12 @@ describe('native.js', function () { sponsoredBy: { required: false, sendTargetingKeys: false + }, + ext: { + foo: { + required: false, + sendTargetingKeys: true + } } } @@ -148,7 +187,8 @@ describe('native.js', function () { CONSTANTS.NATIVE_KEYS.title, CONSTANTS.NATIVE_KEYS.body, CONSTANTS.NATIVE_KEYS.image, - CONSTANTS.NATIVE_KEYS.clickUrl + CONSTANTS.NATIVE_KEYS.clickUrl, + 'hb_native_foo' ]); }); @@ -265,7 +305,7 @@ describe('native.js', function () { const message = getAllAssetsMessage(messageRequest, bid); - expect(message.assets.length).to.equal(7); + expect(message.assets.length).to.equal(9); expect(message.assets).to.deep.include({ key: 'body', value: bid.native.body @@ -294,6 +334,14 @@ describe('native.js', function () { key: 'sponsoredBy', value: bid.native.sponsoredBy }); + expect(message.assets).to.deep.include({ + key: 'foo', + value: bid.native.ext.foo + }); + expect(message.assets).to.deep.include({ + key: 'baz', + value: bid.native.ext.baz + }); }); it('creates native all asset message with only defined fields', function() { @@ -305,7 +353,7 @@ describe('native.js', function () { const message = getAllAssetsMessage(messageRequest, bidWithUndefinedFields); - expect(message.assets.length).to.equal(3); + expect(message.assets.length).to.equal(4); expect(message.assets).to.deep.include({ key: 'clickUrl', value: bid.native.clickUrl @@ -318,6 +366,10 @@ describe('native.js', function () { key: 'sponsoredBy', value: bid.native.sponsoredBy }); + expect(message.assets).to.deep.include({ + key: 'foo', + value: bid.native.ext.foo + }); }); });