diff --git a/modules/yieldlabBidAdapter.js b/modules/yieldlabBidAdapter.js
index c05ecbc15fc..e992dea0c65 100644
--- a/modules/yieldlabBidAdapter.js
+++ b/modules/yieldlabBidAdapter.js
@@ -3,6 +3,7 @@ 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 { Renderer } from '../src/Renderer.js'
+import { config } from '../src/config.js';
const ENDPOINT = 'https://ad.yieldlab.net'
const BIDDER_CODE = 'yieldlab'
@@ -52,6 +53,11 @@ export const spec = {
if (bid.schain && utils.isPlainObject(bid.schain) && Array.isArray(bid.schain.nodes)) {
query.schain = createSchainString(bid.schain)
}
+
+ const iabContent = getContentObject(bid)
+ if (iabContent) {
+ query.iab_content = createIabContentString(iabContent)
+ }
})
if (bidderRequest) {
@@ -105,6 +111,7 @@ export const spec = {
const gdprApplies = reqParams.gdpr ? '&gdpr=' + reqParams.gdpr : ''
const gdprConsent = reqParams.consent ? '&consent=' + reqParams.consent : ''
const pvId = matchedBid.pvid !== undefined ? '&pvid=' + matchedBid.pvid : ''
+ const iabContent = reqParams.iab_content ? '&iab_content=' + reqParams.iab_content : ''
const bidResponse = {
requestId: bidRequest.bidId,
@@ -117,7 +124,7 @@ export const spec = {
netRevenue: false,
ttl: BID_RESPONSE_TTL_SEC,
referrer: '',
- ad: ``,
+ ad: ``,
meta: {
advertiserDomains: (matchedBid.advertiser) ? matchedBid.advertiser : 'n/a'
}
@@ -130,7 +137,7 @@ export const spec = {
bidResponse.height = playersize[1]
}
bidResponse.mediaType = VIDEO
- bidResponse.vastUrl = `${ENDPOINT}/d/${matchedBid.id}/${bidRequest.params.supplyId}/?ts=${timestamp}${extId}${gdprApplies}${gdprConsent}${pvId}`
+ bidResponse.vastUrl = `${ENDPOINT}/d/${matchedBid.id}/${bidRequest.params.supplyId}/?ts=${timestamp}${extId}${gdprApplies}${gdprConsent}${pvId}${iabContent}`
if (isOutstream(bidRequest)) {
const renderer = Renderer.install({
id: bidRequest.bidId,
@@ -211,7 +218,7 @@ function createQueryString (obj) {
for (var p in obj) {
if (obj.hasOwnProperty(p)) {
let val = obj[p]
- if (p !== 'schain') {
+ if (p !== 'schain' && p !== 'iab_content') {
str.push(encodeURIComponent(p) + '=' + encodeURIComponent(val))
} else {
str.push(p + '=' + val)
@@ -253,6 +260,44 @@ function createSchainString (schain) {
return `${ver},${complete}${nodesString}`
}
+/**
+ * Get content object from bid request
+ * First get content from bidder params;
+ * If not provided in bidder params, get from first party data under 'ortb2.site.content' or 'ortb2.app.content'
+ * @param {Object} bid
+ * @returns {Object}
+ */
+function getContentObject(bid) {
+ if (bid.params.iabContent && utils.isPlainObject(bid.params.iabContent)) {
+ return bid.params.iabContent
+ }
+
+ const globalContent = config.getConfig('ortb2.site') ? config.getConfig('ortb2.site.content')
+ : config.getConfig('ortb2.app.content')
+ if (globalContent && utils.isPlainObject(globalContent)) {
+ return globalContent
+ }
+ return undefined
+}
+
+/**
+ * Creates a string for iab_content object
+ * @param {Object} iabContent
+ * @returns {String}
+ */
+function createIabContentString(iabContent) {
+ const arrKeys = ['keywords', 'cat']
+ let str = []
+ for (let key in iabContent) {
+ if (iabContent.hasOwnProperty(key)) {
+ const value = (arrKeys.indexOf(key) !== -1 && Array.isArray(iabContent[key]))
+ ? iabContent[key].map(node => encodeURIComponent(node)).join('|') : encodeURIComponent(iabContent[key])
+ str.push(''.concat(key, ':', value))
+ }
+ }
+ return encodeURIComponent(str.join(','))
+}
+
/**
* Encodes URI Component with exlamation mark included. Needed for schain object.
* @param {String} str
diff --git a/modules/yieldlabBidAdapter.md b/modules/yieldlabBidAdapter.md
index a7a3f2715dc..e3360ab10be 100644
--- a/modules/yieldlabBidAdapter.md
+++ b/modules/yieldlabBidAdapter.md
@@ -25,7 +25,22 @@ Module that connects to Yieldlab's demand sources
key1: "value1",
key2: "value2"
},
- extId: "abc"
+ 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"
+ }
}
}]
}, {
diff --git a/test/spec/modules/yieldlabBidAdapter_spec.js b/test/spec/modules/yieldlabBidAdapter_spec.js
index d150646851f..698bfb92888 100644
--- a/test/spec/modules/yieldlabBidAdapter_spec.js
+++ b/test/spec/modules/yieldlabBidAdapter_spec.js
@@ -1,3 +1,4 @@
+import { config } from 'src/config.js';
import { expect } from 'chai'
import { spec } from 'modules/yieldlabBidAdapter.js'
import { newBidder } from 'src/adapters/bidderFactory.js'
@@ -16,7 +17,22 @@ const REQUEST = {
'extraParam': true,
'foo': 'bar'
},
- 'extId': 'abc'
+ 'extId': 'abc',
+ 'iabContent': {
+ 'id': 'foo_id',
+ 'episode': '99',
+ 'title': 'foo_title,bar_title',
+ 'series': 'foo_series',
+ 'season': 's1',
+ 'artist': 'foo bar',
+ 'genre': 'baz',
+ 'isrc': 'CC-XXX-YY-NNNNN',
+ 'url': 'http://foo_url.de',
+ 'cat': ['cat1', 'cat2,ppp', 'cat3|||//'],
+ 'context': '7',
+ 'keywords': ['k1,', 'k2..'],
+ 'live': '0'
+ }
},
'bidderRequestId': '143346cf0f1731',
'auctionId': '2e41f65424c87c',
@@ -86,6 +102,10 @@ const REQPARAMS_GDPR = Object.assign({}, REQPARAMS, {
consent: 'BN5lERiOMYEdiAKAWXEND1AAAAE6DABACMA'
})
+const REQPARAMS_IAB_CONTENT = Object.assign({}, REQPARAMS, {
+ iab_content: 'id%3Afoo_id%2Cepisode%3A99%2Ctitle%3Afoo_title%252Cbar_title%2Cseries%3Afoo_series%2Cseason%3As1%2Cartist%3Afoo%2520bar%2Cgenre%3Abaz%2Cisrc%3ACC-XXX-YY-NNNNN%2Curl%3Ahttp%253A%252F%252Ffoo_url.de%2Ccat%3Acat1%7Ccat2%252Cppp%7Ccat3%257C%257C%257C%252F%252F%2Ccontext%3A7%2Ckeywords%3Ak1%252C%7Ck2..%2Clive%3A0'
+})
+
describe('yieldlabBidAdapter', function () {
const adapter = newBidder(spec)
@@ -139,6 +159,40 @@ describe('yieldlabBidAdapter', function () {
expect(request.url).to.include('schain=1.0,1!indirectseller.com,1,1,,,,!indirectseller2.com,2,1,,indirectseller2%20name%20with%20comma%20%2C%20and%20bang%20%21,,')
})
+ it('passes iab_content string to bid request', function () {
+ expect(request.url).to.include('iab_content=id%3Afoo_id%2Cepisode%3A99%2Ctitle%3Afoo_title%252Cbar_title%2Cseries%3Afoo_series%2Cseason%3As1%2Cartist%3Afoo%2520bar%2Cgenre%3Abaz%2Cisrc%3ACC-XXX-YY-NNNNN%2Curl%3Ahttp%253A%252F%252Ffoo_url.de%2Ccat%3Acat1%7Ccat2%252Cppp%7Ccat3%257C%257C%257C%252F%252F%2Ccontext%3A7%2Ckeywords%3Ak1%252C%7Ck2..%2Clive%3A0')
+ })
+
+ const siteConfig = {
+ 'ortb2': {
+ 'site': {
+ 'content': {
+ 'id': 'id_from_config'
+ }
+ }
+ }
+ }
+
+ it('generates iab_content string from bidder params', function () {
+ config.setConfig(siteConfig);
+ const request = spec.buildRequests([REQUEST])
+ expect(request.url).to.include('iab_content=id%3Afoo_id%2Cepisode%3A99%2Ctitle%3Afoo_title%252Cbar_title%2Cseries%3Afoo_series%2Cseason%3As1%2Cartist%3Afoo%2520bar%2Cgenre%3Abaz%2Cisrc%3ACC-XXX-YY-NNNNN%2Curl%3Ahttp%253A%252F%252Ffoo_url.de%2Ccat%3Acat1%7Ccat2%252Cppp%7Ccat3%257C%257C%257C%252F%252F%2Ccontext%3A7%2Ckeywords%3Ak1%252C%7Ck2..%2Clive%3A0')
+ config.resetConfig();
+ })
+
+ it('generates iab_content string from first party data if not provided in bidder params', function () {
+ const requestWithoutIabContent = {
+ 'params': {
+ 'adslotId': '1111',
+ 'supplyId': '2222'
+ }
+ }
+ config.setConfig(siteConfig);
+ const request = spec.buildRequests([requestWithoutIabContent])
+ expect(request.url).to.include('iab_content=id%3Aid_from_config')
+ config.resetConfig();
+ })
+
const refererRequest = spec.buildRequests(bidRequests, {
refererInfo: {
canonicalUrl: undefined,
@@ -203,6 +257,11 @@ describe('yieldlabBidAdapter', function () {
expect(result[0].ad).to.include('&consent=BN5lERiOMYEdiAKAWXEND1AAAAE6DABACMA')
})
+ it('should append iab_content to adtag', function () {
+ const result = spec.interpretResponse({body: [RESPONSE]}, {validBidRequests: [REQUEST], queryParams: REQPARAMS_IAB_CONTENT})
+ expect(result[0].ad).to.include('&iab_content=id%3Afoo_id%2Cepisode%3A99%2Ctitle%3Afoo_title%252Cbar_title%2Cseries%3Afoo_series%2Cseason%3As1%2Cartist%3Afoo%2520bar%2Cgenre%3Abaz%2Cisrc%3ACC-XXX-YY-NNNNN%2Curl%3Ahttp%253A%252F%252Ffoo_url.de%2Ccat%3Acat1%7Ccat2%252Cppp%7Ccat3%257C%257C%257C%252F%252F%2Ccontext%3A7%2Ckeywords%3Ak1%252C%7Ck2..%2Clive%3A0')
+ })
+
it('should get correct bid response when passing more than one size', function () {
const REQUEST2 = Object.assign({}, REQUEST, {
'sizes': [
@@ -268,5 +327,10 @@ describe('yieldlabBidAdapter', function () {
expect(result[0].ad).to.include('&pvid=43513f11-55a0-4a83-94e5-0ebc08f54a2c')
expect(result[0].vastUrl).to.include('&pvid=43513f11-55a0-4a83-94e5-0ebc08f54a2c')
})
+
+ it('should append iab_content to vastUrl', function () {
+ const result = spec.interpretResponse({body: [VIDEO_RESPONSE]}, {validBidRequests: [VIDEO_REQUEST], queryParams: REQPARAMS_IAB_CONTENT})
+ expect(result[0].vastUrl).to.include('&iab_content=id%3Afoo_id%2Cepisode%3A99%2Ctitle%3Afoo_title%252Cbar_title%2Cseries%3Afoo_series%2Cseason%3As1%2Cartist%3Afoo%2520bar%2Cgenre%3Abaz%2Cisrc%3ACC-XXX-YY-NNNNN%2Curl%3Ahttp%253A%252F%252Ffoo_url.de%2Ccat%3Acat1%7Ccat2%252Cppp%7Ccat3%257C%257C%257C%252F%252F%2Ccontext%3A7%2Ckeywords%3Ak1%252C%7Ck2..%2Clive%3A0')
+ })
})
})