From 320f9a2384a6d51486471d36586277aeb9d3b9f6 Mon Sep 17 00:00:00 2001 From: Aleksei Shashin <90848837+alex-ylb@users.noreply.github.com> Date: Thu, 21 Oct 2021 11:36:32 +0200 Subject: [PATCH 1/9] YL-3989: Accept NATIVE yieldprobe response (#2) * YL-3989: Accept NATIVE response --- modules/yieldlabBidAdapter.js | 35 ++++++++- modules/yieldlabBidAdapter.md | 13 ++++ test/spec/modules/yieldlabBidAdapter_spec.js | 81 ++++++++++++++++++++ 3 files changed, 127 insertions(+), 2 deletions(-) diff --git a/modules/yieldlabBidAdapter.js b/modules/yieldlabBidAdapter.js index 994098cf5c8..ce42e7f2a97 100644 --- a/modules/yieldlabBidAdapter.js +++ b/modules/yieldlabBidAdapter.js @@ -1,7 +1,7 @@ import { _each, isPlainObject, isArray, deepAccess } from '../src/utils.js'; import { registerBidder } from '../src/adapters/bidderFactory.js' import find from 'core-js-pure/features/array/find.js' -import { VIDEO, BANNER } from '../src/mediaTypes.js' +import { VIDEO, BANNER, NATIVE } from '../src/mediaTypes.js' import { Renderer } from '../src/Renderer.js' import { config } from '../src/config.js'; @@ -15,7 +15,7 @@ const GVLID = 70 export const spec = { code: BIDDER_CODE, gvlid: GVLID, - supportedMediaTypes: [VIDEO, BANNER], + supportedMediaTypes: [VIDEO, BANNER, NATIVE], isBidRequestValid: function (bid) { if (bid && bid.params && bid.params.adslotId && bid.params.supplyId) { @@ -149,6 +149,27 @@ export const spec = { } } + if (isNative(bidRequest, adType)) { + const url = `${ENDPOINT}/d/${matchedBid.id}/${bidRequest.params.supplyId}/?ts=${timestamp}${extId}${gdprApplies}${gdprConsent}${pvId}` + bidResponse.adUrl = url + bidResponse.mediaType = NATIVE + const nativeImageAssetObj = matchedBid.native.assets.find(e => e.id === 2) + const nativeImageAsset = nativeImageAssetObj ? nativeImageAssetObj.img : {url: '', w: 0, h: 0}; + const nativeTitleAsset = matchedBid.native.assets.find(e => e.id === 1) + const nativeBodyAsset = matchedBid.native.assets.find(e => e.id === 3) + bidResponse.native = { + title: nativeTitleAsset ? nativeTitleAsset.title.text : '', + body: nativeBodyAsset ? nativeBodyAsset.data.value : '', + image: { + url: nativeImageAsset.url, + width: nativeImageAsset.w, + height: nativeImageAsset.h, + }, + clickUrl: matchedBid.native.link.url, + impressionTrackers: matchedBid.native.imptrackers, + }; + } + bidResponses.push(bidResponse) } }) @@ -166,6 +187,16 @@ function isVideo (format, adtype) { return deepAccess(format, 'mediaTypes.video') && adtype.toLowerCase() === 'video' } +/** + * Is this a native format? + * @param {Object} format + * @param {String} adtype + * @returns {Boolean} + */ +function isNative(format, adtype) { + return utils.deepAccess(format, 'mediaTypes.native') && adtype.toLowerCase() === 'native' +} + /** * Is this an outstream context? * @param {Object} format diff --git a/modules/yieldlabBidAdapter.md b/modules/yieldlabBidAdapter.md index e3360ab10be..f2dbdc39895 100644 --- a/modules/yieldlabBidAdapter.md +++ b/modules/yieldlabBidAdapter.md @@ -58,6 +58,19 @@ Module that connects to Yieldlab's demand sources supplyId: "1381604" } }] + }, + { + code: "native", + mediaTypes: { + native: { } + }, + bids: [{ + bidder: "yieldlab", + params: { + adslotId: "5220339", + supplyId: "1381604" + } + }] } ]; ``` diff --git a/test/spec/modules/yieldlabBidAdapter_spec.js b/test/spec/modules/yieldlabBidAdapter_spec.js index 698bfb92888..f80cad46d50 100644 --- a/test/spec/modules/yieldlabBidAdapter_spec.js +++ b/test/spec/modules/yieldlabBidAdapter_spec.js @@ -73,6 +73,12 @@ const VIDEO_REQUEST = Object.assign({}, REQUEST, { } }) +const NATIVE_REQUEST = Object.assign({}, REQUEST, { + 'mediaTypes': { + 'native': { } + } +}) + const RESPONSE = { advertiser: 'yieldlab', curl: 'https://www.yieldlab.de', @@ -84,6 +90,42 @@ const RESPONSE = { adtype: 'BANNER' } +const NATIVE_RESPONSE = Object.assign({}, RESPONSE, { + 'adtype': 'NATIVE', + 'native': { + 'link': { + 'url': 'https://www.yieldlab.de' + }, + 'assets': [ + { + 'id': 1, + 'title': { + 'text': 'This is a great headline' + } + }, + { + 'id': 2, + 'img': { + 'url': 'https://localhost:8080/yl-logo100x100.jpg', + 'w': 100, + 'h': 100 + } + }, + { + 'id': 3, + 'data': { + 'value': 'Native body value' + } + } + ], + 'imptrackers': [ + 'http://localhost:8080/ve?d=ODE9ZSY2MTI1MjAzNjMzMzYxPXN0JjA0NWUwZDk0NTY5Yi05M2FiLWUwZTQtOWFjNy1hYWY0MzFiZj1kaXQmMj12', + 'http://localhost:8080/md/1111/9efa4e76-2030-4f04-bb9f-322541f8d611?mdata=false&pvid=false&ids=x:1', + 'http://localhost:8080/imp?s=13216&d=2171514&a=12548955&ts=1633363025216&tid=fb134faa-7ca9-4e0e-ba39-b96549d0e540&l=0' + ] + } +}) + const VIDEO_RESPONSE = Object.assign({}, RESPONSE, { 'adtype': 'VIDEO' }) @@ -297,6 +339,45 @@ describe('yieldlabBidAdapter', function () { expect(result[0].vastUrl).to.include('&id=abc') }) + it('should add adUrl and native assets when type is Native', function () { + const result = spec.interpretResponse({body: [NATIVE_RESPONSE]}, {validBidRequests: [NATIVE_REQUEST], queryParams: REQPARAMS}) + + expect(result[0].requestId).to.equal('2d925f27f5079f') + expect(result[0].cpm).to.equal(0.01) + expect(result[0].mediaType).to.equal('native') + expect(result[0].adUrl).to.include('https://ad.yieldlab.net/d/1111/2222/?ts=') + expect(result[0].native.title).to.equal('This is a great headline') + expect(result[0].native.body).to.equal('Native body value') + expect(result[0].native.image.url).to.equal('https://localhost:8080/yl-logo100x100.jpg') + expect(result[0].native.image.width).to.equal(100) + expect(result[0].native.image.height).to.equal(100) + expect(result[0].native.clickUrl).to.equal('https://www.yieldlab.de') + expect(result[0].native.impressionTrackers.length).to.equal(3) + }) + + it('should add adUrl and default native assets when type is Native', function () { + const NATIVE_RESPONSE_2 = Object.assign({}, NATIVE_RESPONSE, { + 'native': { + 'link': { + 'url': 'https://www.yieldlab.de' + }, + 'assets': [], + 'imptrackers': [] + } + }) + const result = spec.interpretResponse({body: [NATIVE_RESPONSE_2]}, {validBidRequests: [NATIVE_REQUEST], queryParams: REQPARAMS}) + + expect(result[0].requestId).to.equal('2d925f27f5079f') + expect(result[0].cpm).to.equal(0.01) + expect(result[0].mediaType).to.equal('native') + expect(result[0].adUrl).to.include('https://ad.yieldlab.net/d/1111/2222/?ts=') + expect(result[0].native.title).to.equal('') + expect(result[0].native.body).to.equal('') + expect(result[0].native.image.url).to.equal('') + expect(result[0].native.image.width).to.equal(0) + expect(result[0].native.image.height).to.equal(0) + }) + it('should append gdpr parameters to vastUrl', function () { const result = spec.interpretResponse({body: [VIDEO_RESPONSE]}, {validBidRequests: [VIDEO_REQUEST], queryParams: REQPARAMS_GDPR}) From ab5020c8eee5051b53e83b18b15c080022f6677a Mon Sep 17 00:00:00 2001 From: Aleksei Shashin Date: Thu, 21 Oct 2021 13:11:25 +0200 Subject: [PATCH 2/9] Fix: 'utils' is not defined no-undef --- modules/yieldlabBidAdapter.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/yieldlabBidAdapter.js b/modules/yieldlabBidAdapter.js index ce42e7f2a97..f469f030b8d 100644 --- a/modules/yieldlabBidAdapter.js +++ b/modules/yieldlabBidAdapter.js @@ -194,7 +194,7 @@ function isVideo (format, adtype) { * @returns {Boolean} */ function isNative(format, adtype) { - return utils.deepAccess(format, 'mediaTypes.native') && adtype.toLowerCase() === 'native' + return deepAccess(format, 'mediaTypes.native') && adtype.toLowerCase() === 'native' } /** From 11c410ca0eee740d7a975db5032056516975f7c9 Mon Sep 17 00:00:00 2001 From: Aleksei Shashin Date: Fri, 22 Oct 2021 09:52:20 +0200 Subject: [PATCH 3/9] trigger GitHub actions From 8d1602f9eb20e4b7f97ebce9228b8d1bd510166d Mon Sep 17 00:00:00 2001 From: kippsterr <29540638+kippsterr@users.noreply.github.com> Date: Wed, 27 Oct 2021 09:01:30 +0200 Subject: [PATCH 4/9] Add multi-format example to the Yieldlab bidder documentation --- modules/yieldlabBidAdapter.md | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/modules/yieldlabBidAdapter.md b/modules/yieldlabBidAdapter.md index f2dbdc39895..7dc1972fe3c 100644 --- a/modules/yieldlabBidAdapter.md +++ b/modules/yieldlabBidAdapter.md @@ -74,3 +74,29 @@ Module that connects to Yieldlab's demand sources } ]; ``` + +# Multi-Format Setup + +A general overview of how to set up multi-format ads can be found in the offical Predid.js docs. See: [show multi-format ads](https://docs.prebid.org/dev-docs/show-multi-format-ads.html) + +When setting up multi-format ads with Yieldlab make sure to always add at least one eligible Adslot per given media type in the ad unit configuration. + +```javascript +const adUnit = { + code: 'multi-format-adslot', + mediaTypes: { + banner: { + sizes: [ [ 728, 90 ] ] + }, + native: { + // native config + } + }, + bids: [ + // banner adslot + { bidder: 'yieldlab', params: { adslotId: '1234', supplyId: '42' } }, + // native adslot + { bidder: 'yieldlab', params: { adslotId: '2345', supplyId: '42' } } + ] +}; +``` From e51982d7dae8aa9feeb2cb42b737a18b19e760ae Mon Sep 17 00:00:00 2001 From: kippsterr <29540638+kippsterr@users.noreply.github.com> Date: Wed, 27 Oct 2021 09:07:36 +0200 Subject: [PATCH 5/9] Reformat code --- modules/yieldlabBidAdapter.md | 132 +++++++++++++++++----------------- 1 file changed, 68 insertions(+), 64 deletions(-) diff --git a/modules/yieldlabBidAdapter.md b/modules/yieldlabBidAdapter.md index 7dc1972fe3c..7c393405252 100644 --- a/modules/yieldlabBidAdapter.md +++ b/modules/yieldlabBidAdapter.md @@ -11,68 +11,72 @@ Maintainer: solutions@yieldlab.de Module that connects to Yieldlab's demand sources # Test Parameters -``` - var adUnits = [ - { - code: "banner", - sizes: [[728, 90]], - bids: [{ - bidder: "yieldlab", - params: { - adslotId: "5220336", - supplyId: "1381604", - targeting: { - key1: "value1", - key2: "value2" - }, - extId: "abc", - iabContent: { - id: "some_id", - episode: "1", - title: "some title", - series: "some series", - season: "s1", - artist: "John Doe", - genre: "some genre", - isrc: "CC-XXX-YY-NNNNN", - url: "http://foo_url.de", - cat: ["IAB1-1", "IAB1-2", "IAB2-10"], - context: "7", - keywords: ["k1", "k2"], - live: "0" - } - } - }] - }, { - code: "video", - sizes: [[640, 480]], - mediaTypes: { - video: { - context: "instream" // or "outstream" - } - }, - bids: [{ - bidder: "yieldlab", - params: { - adslotId: "5220339", - supplyId: "1381604" - } - }] - }, - { - code: "native", - mediaTypes: { - native: { } - }, - bids: [{ - bidder: "yieldlab", - params: { - adslotId: "5220339", - supplyId: "1381604" - } - }] - } - ]; + +```javascript +const adUnits = [ + { + code: 'banner', + sizes: [ [ 728, 90 ] ], + bids: [{ + bidder: 'yieldlab', + params: { + adslotId: '5220336', + supplyId: '1381604', + targeting: { + key1: 'value1', + key2: 'value2' + }, + extId: 'abc', + iabContent: { + id: 'some_id', + episode: '1', + title: 'some title', + series: 'some series', + season: 's1', + artist: 'John Doe', + genre: 'some genre', + isrc: 'CC-XXX-YY-NNNNN', + url: 'http://foo_url.de', + cat: [ 'IAB1-1', 'IAB1-2', 'IAB2-10' ], + context: '7', + keywords: ['k1', 'k2'], + live: '0' + } + } + }] + }, + { + code: 'video', + sizes: [ [ 640, 480 ] ], + mediaTypes: { + video: { + context: 'instream' // or 'outstream' + } + }, + bids: [{ + bidder: 'yieldlab', + params: { + adslotId: '5220339', + supplyId: '1381604' + } + }] + }, + { + code: 'native', + mediaTypes: { + native: { + // native config + } + }, + bids: [{ + bidder: 'yieldlab', + params: { + adslotId: '5220339', + supplyId: '1381604' + } + }] + } +]; ``` # Multi-Format Setup @@ -93,9 +97,9 @@ const adUnit = { } }, bids: [ - // banner adslot + // banner Adslot { bidder: 'yieldlab', params: { adslotId: '1234', supplyId: '42' } }, - // native adslot + // native Adslot { bidder: 'yieldlab', params: { adslotId: '2345', supplyId: '42' } } ] }; From 6b12082f15130c9731a2bd8f284f71f430bbd85b Mon Sep 17 00:00:00 2001 From: Aleksei Shashin Date: Wed, 27 Oct 2021 10:21:29 +0200 Subject: [PATCH 6/9] Fix: Object doesn't support 'find' Object doesn't support property or method 'find' in IE 11 --- modules/yieldlabBidAdapter.js | 28 +++++++++++++++++----------- 1 file changed, 17 insertions(+), 11 deletions(-) diff --git a/modules/yieldlabBidAdapter.js b/modules/yieldlabBidAdapter.js index f469f030b8d..9eeaf02ef24 100644 --- a/modules/yieldlabBidAdapter.js +++ b/modules/yieldlabBidAdapter.js @@ -153,10 +153,16 @@ export const spec = { const url = `${ENDPOINT}/d/${matchedBid.id}/${bidRequest.params.supplyId}/?ts=${timestamp}${extId}${gdprApplies}${gdprConsent}${pvId}` bidResponse.adUrl = url bidResponse.mediaType = NATIVE - const nativeImageAssetObj = matchedBid.native.assets.find(e => e.id === 2) + const nativeImageAssetObj = matchedBid.native.assets.filter(function (e) { + return e.id === 2 + })[0]; const nativeImageAsset = nativeImageAssetObj ? nativeImageAssetObj.img : {url: '', w: 0, h: 0}; - const nativeTitleAsset = matchedBid.native.assets.find(e => e.id === 1) - const nativeBodyAsset = matchedBid.native.assets.find(e => e.id === 3) + const nativeTitleAsset = matchedBid.native.assets.filter(function (e) { + return e.id === 1 + })[0]; + const nativeBodyAsset = matchedBid.native.assets.filter(function (e) { + return e.id === 3 + })[0]; bidResponse.native = { title: nativeTitleAsset ? nativeTitleAsset.title.text : '', body: nativeBodyAsset ? nativeBodyAsset.data.value : '', @@ -183,7 +189,7 @@ export const spec = { * @param {String} adtype * @returns {Boolean} */ -function isVideo (format, adtype) { +function isVideo(format, adtype) { return deepAccess(format, 'mediaTypes.video') && adtype.toLowerCase() === 'video' } @@ -202,7 +208,7 @@ function isNative(format, adtype) { * @param {Object} format * @returns {Boolean} */ -function isOutstream (format) { +function isOutstream(format) { let context = deepAccess(format, 'mediaTypes.video.context') return (context === 'outstream') } @@ -212,7 +218,7 @@ function isOutstream (format) { * @param {Object} format * @returns {Array} */ -function getPlayerSize (format) { +function getPlayerSize(format) { let playerSize = deepAccess(format, 'mediaTypes.video.playerSize') return (playerSize && isArray(playerSize[0])) ? playerSize[0] : playerSize } @@ -222,7 +228,7 @@ function getPlayerSize (format) { * @param {String} size * @returns {Array} */ -function parseSize (size) { +function parseSize(size) { return size.split('x').map(Number) } @@ -231,7 +237,7 @@ function parseSize (size) { * @param {Array} eids * @returns {String} */ -function createUserIdString (eids) { +function createUserIdString(eids) { let str = [] for (let i = 0; i < eids.length; i++) { str.push(eids[i].source + ':' + eids[i].uids[0].id) @@ -244,7 +250,7 @@ function createUserIdString (eids) { * @param {Object} obj * @returns {String} */ -function createQueryString (obj) { +function createQueryString(obj) { let str = [] for (var p in obj) { if (obj.hasOwnProperty(p)) { @@ -264,7 +270,7 @@ function createQueryString (obj) { * @param {Object} obj * @returns {String} */ -function createTargetingString (obj) { +function createTargetingString(obj) { let str = [] for (var p in obj) { if (obj.hasOwnProperty(p)) { @@ -281,7 +287,7 @@ function createTargetingString (obj) { * @param {Object} schain * @returns {String} */ -function createSchainString (schain) { +function createSchainString(schain) { const ver = schain.ver || '' const complete = (schain.complete === 1 || schain.complete === 0) ? schain.complete : '' const keys = ['asi', 'sid', 'hp', 'rid', 'name', 'domain', 'ext'] From 97b0e6548b76bdcf7bf301205e148bf67f2398f4 Mon Sep 17 00:00:00 2001 From: Aleksei Shashin Date: Wed, 27 Oct 2021 10:32:40 +0200 Subject: [PATCH 7/9] trigger GitHub actions From 1e1a1bda719f38014eb0789f0c7cc57110bf1c01 Mon Sep 17 00:00:00 2001 From: Aleksei Shashin Date: Wed, 27 Oct 2021 12:05:55 +0200 Subject: [PATCH 8/9] Chore:Replace `filter` by `find` from ..array/find.js --- modules/yieldlabBidAdapter.js | 12 +++--------- 1 file changed, 3 insertions(+), 9 deletions(-) diff --git a/modules/yieldlabBidAdapter.js b/modules/yieldlabBidAdapter.js index 9eeaf02ef24..c2f2b79a3b7 100644 --- a/modules/yieldlabBidAdapter.js +++ b/modules/yieldlabBidAdapter.js @@ -153,16 +153,10 @@ export const spec = { const url = `${ENDPOINT}/d/${matchedBid.id}/${bidRequest.params.supplyId}/?ts=${timestamp}${extId}${gdprApplies}${gdprConsent}${pvId}` bidResponse.adUrl = url bidResponse.mediaType = NATIVE - const nativeImageAssetObj = matchedBid.native.assets.filter(function (e) { - return e.id === 2 - })[0]; + const nativeImageAssetObj = find(matchedBid.native.assets, e => e.id === 2) const nativeImageAsset = nativeImageAssetObj ? nativeImageAssetObj.img : {url: '', w: 0, h: 0}; - const nativeTitleAsset = matchedBid.native.assets.filter(function (e) { - return e.id === 1 - })[0]; - const nativeBodyAsset = matchedBid.native.assets.filter(function (e) { - return e.id === 3 - })[0]; + const nativeTitleAsset = find(matchedBid.native.assets, e => e.id === 1) + const nativeBodyAsset = find(matchedBid.native.assets, e => e.id === 3) bidResponse.native = { title: nativeTitleAsset ? nativeTitleAsset.title.text : '', body: nativeBodyAsset ? nativeBodyAsset.data.value : '', From 840b1bf0d822d71d6a7edfe42f437829980977b5 Mon Sep 17 00:00:00 2001 From: kippsterr <29540638+kippsterr@users.noreply.github.com> Date: Wed, 27 Oct 2021 15:06:45 +0200 Subject: [PATCH 9/9] Fix typo --- modules/yieldlabBidAdapter.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/yieldlabBidAdapter.md b/modules/yieldlabBidAdapter.md index 7c393405252..1f52e26f5c7 100644 --- a/modules/yieldlabBidAdapter.md +++ b/modules/yieldlabBidAdapter.md @@ -81,7 +81,7 @@ const adUnits = [ # Multi-Format Setup -A general overview of how to set up multi-format ads can be found in the offical Predid.js docs. See: [show multi-format ads](https://docs.prebid.org/dev-docs/show-multi-format-ads.html) +A general overview of how to set up multi-format ads can be found in the offical Prebid.js docs. See: [show multi-format ads](https://docs.prebid.org/dev-docs/show-multi-format-ads.html) When setting up multi-format ads with Yieldlab make sure to always add at least one eligible Adslot per given media type in the ad unit configuration.