Skip to content

Commit

Permalink
Yieldlab Adapter: add support for iab content (#7413)
Browse files Browse the repository at this point in the history
* Yieldlab Adapter: add support for iab content

* Yieldlab Adapter: use array.indexOf instead of array.includes

* Yieldlab Adapter: support content object from first party data

Co-authored-by: Yu Tong <y.tong@yieldlab.de>
  • Loading branch information
rey1128 and Yu Tong authored Oct 12, 2021
1 parent e54ec61 commit 424d9a0
Show file tree
Hide file tree
Showing 3 changed files with 129 additions and 5 deletions.
51 changes: 48 additions & 3 deletions modules/yieldlabBidAdapter.js
Original file line number Diff line number Diff line change
Expand Up @@ -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'
Expand Down Expand Up @@ -52,6 +53,11 @@ export const spec = {
if (bid.schain && 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) {
Expand Down Expand Up @@ -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,
Expand All @@ -117,7 +124,7 @@ export const spec = {
netRevenue: false,
ttl: BID_RESPONSE_TTL_SEC,
referrer: '',
ad: `<script src="${ENDPOINT}/d/${matchedBid.id}/${bidRequest.params.supplyId}/?ts=${timestamp}${extId}${gdprApplies}${gdprConsent}${pvId}"></script>`,
ad: `<script src="${ENDPOINT}/d/${matchedBid.id}/${bidRequest.params.supplyId}/?ts=${timestamp}${extId}${gdprApplies}${gdprConsent}${pvId}${iabContent}"></script>`,
meta: {
advertiserDomains: (matchedBid.advertiser) ? matchedBid.advertiser : 'n/a'
}
Expand All @@ -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,
Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -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
Expand Down
17 changes: 16 additions & 1 deletion modules/yieldlabBidAdapter.md
Original file line number Diff line number Diff line change
Expand Up @@ -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"
}
}
}]
}, {
Expand Down
66 changes: 65 additions & 1 deletion test/spec/modules/yieldlabBidAdapter_spec.js
Original file line number Diff line number Diff line change
@@ -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'
Expand All @@ -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',
Expand Down Expand Up @@ -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)

Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -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': [
Expand Down Expand Up @@ -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')
})
})
})

0 comments on commit 424d9a0

Please sign in to comment.