Skip to content

Commit

Permalink
Yieldlab Bid Adapter: add support for native mediatype (prebid#7609)
Browse files Browse the repository at this point in the history
* YL-3989: Accept NATIVE yieldprobe response (#2)

* YL-3989: Accept NATIVE response

* Fix: 'utils' is not defined  no-undef

* trigger GitHub actions

* Add multi-format example to the Yieldlab bidder documentation

* Reformat code

* Fix: Object doesn't support 'find'

Object doesn't support property or method 'find' in IE 11

* trigger GitHub actions

* Chore:Replace `filter` by `find` from ..array/find.js

* Fix typo

Co-authored-by: Christoph <[email protected]>
  • Loading branch information
alex-ylb and kippsterr authored Oct 29, 2021
1 parent 199eb7d commit 388ddee
Show file tree
Hide file tree
Showing 3 changed files with 213 additions and 58 deletions.
51 changes: 41 additions & 10 deletions modules/yieldlabBidAdapter.js
Original file line number Diff line number Diff line change
@@ -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';

Expand All @@ -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) {
Expand Down Expand Up @@ -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 = find(matchedBid.native.assets, e => e.id === 2)
const nativeImageAsset = nativeImageAssetObj ? nativeImageAssetObj.img : {url: '', w: 0, h: 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 : '',
image: {
url: nativeImageAsset.url,
width: nativeImageAsset.w,
height: nativeImageAsset.h,
},
clickUrl: matchedBid.native.link.url,
impressionTrackers: matchedBid.native.imptrackers,
};
}

bidResponses.push(bidResponse)
}
})
Expand All @@ -162,16 +183,26 @@ export const spec = {
* @param {String} adtype
* @returns {Boolean}
*/
function isVideo (format, adtype) {
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 deepAccess(format, 'mediaTypes.native') && adtype.toLowerCase() === 'native'
}

/**
* Is this an outstream context?
* @param {Object} format
* @returns {Boolean}
*/
function isOutstream (format) {
function isOutstream(format) {
let context = deepAccess(format, 'mediaTypes.video.context')
return (context === 'outstream')
}
Expand All @@ -181,7 +212,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
}
Expand All @@ -191,7 +222,7 @@ function getPlayerSize (format) {
* @param {String} size
* @returns {Array}
*/
function parseSize (size) {
function parseSize(size) {
return size.split('x').map(Number)
}

Expand All @@ -200,7 +231,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)
Expand All @@ -213,7 +244,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)) {
Expand All @@ -233,7 +264,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)) {
Expand All @@ -250,7 +281,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']
Expand Down
139 changes: 91 additions & 48 deletions modules/yieldlabBidAdapter.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,53 +11,96 @@ Maintainer: [email protected]
Module that connects to Yieldlab's demand sources

# Test Parameters

```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'
}
}]
}
];
```
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"
}
}]
}
];

# Multi-Format Setup

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.

```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' } }
]
};
```
81 changes: 81 additions & 0 deletions test/spec/modules/yieldlabBidAdapter_spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -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',
Expand All @@ -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'
})
Expand Down Expand Up @@ -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})

Expand Down

0 comments on commit 388ddee

Please sign in to comment.