Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add Yieldlab adapter #1967

Merged
merged 3 commits into from
Jan 25, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
107 changes: 107 additions & 0 deletions modules/yieldlabBidAdapter.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
import * as utils from 'src/utils'
import { registerBidder } from 'src/adapters/bidderFactory'
import find from 'core-js/library/fn/array/find'
import { VIDEO, BANNER } from 'src/mediaTypes'

const ENDPOINT = 'https://ad.yieldlab.net'
const BIDDER_CODE = 'yieldlab'
const BID_RESPONSE_TTL_SEC = 600
const CURRENCY_CODE = 'EUR'

export const spec = {
code: BIDDER_CODE,
supportedMediaTypes: [VIDEO, BANNER],
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

banner is default so no need to add in supportedMediaTypes

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@GEMI Sorry for the confusion. Can you please add BANNER again.
It will be required once #1991 is merged


isBidRequestValid: function (bid) {
if (bid && bid.params && bid.params.placementId && bid.params.adSize) {
return true
}
return false
},

/**
* This method should build correct URL
* @param validBidRequests
* @returns {{method: string, url: string}}
*/
buildRequests: function (validBidRequests) {
const placementIds = []
const timestamp = Date.now()

utils._each(validBidRequests, function (bid) {
placementIds.push(bid.params.placementId)
})

const placements = placementIds.join(',')

return {
method: 'GET',
url: `${ENDPOINT}/yp/${placements}?ts=${timestamp}&json=true`,
validBidRequests: validBidRequests
}
},

/**
* Map ad values and pricing and stuff
* @param serverResponse
* @param originalBidRequest
*/
interpretResponse: function (serverResponse, originalBidRequest) {
const bidResponses = []
const timestamp = Date.now()

originalBidRequest.validBidRequests.forEach(function (bidRequest) {
if (!serverResponse.body) {
return
}

let matchedBid = find(serverResponse.body, function (bidResponse) {
return bidRequest.params.placementId == bidResponse.id
})

if (matchedBid) {
const sizes = parseSize(bidRequest.params.adSize)
const bidResponse = {
requestId: bidRequest.bidId,
cpm: matchedBid.price / 100,
width: sizes[0],
height: sizes[1],
creativeId: '' + matchedBid.id,
dealId: matchedBid.did,
currency: CURRENCY_CODE,
netRevenue: true,
ttl: BID_RESPONSE_TTL_SEC,
referrer: '',
ad: `<script src="${ENDPOINT}/d/${matchedBid.id}/${bidRequest.params.accountId}/${sizes[0]}x${sizes[1]}?ts=${timestamp}"></script>`
}
if (isVideo(bidRequest)) {
bidResponse.mediaType = VIDEO
bidResponse.vastUrl = `${ENDPOINT}/d/${matchedBid.id}/${bidRequest.params.accountId}/1x1?ts=${timestamp}`
}

bidResponses.push(bidResponse)
}
})
return bidResponses
}
};

/**
* Is this a video format?
* @param {String} format
* @returns {Boolean}
*/
function isVideo (format) {
return utils.deepAccess(format, 'mediaTypes.video')
}

/**
* Expands a 'WxH' string as a 2-element [W, H] array
* @param {String} size
* @returns {Array}
*/
function parseSize (size) {
return size.split('x').map(Number)
}

registerBidder(spec)
45 changes: 45 additions & 0 deletions modules/yieldlabBidAdapter.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
# Overview

```
Module Name: Yieldlab Bidder Adapter
Module Type: Bidder Adapter
Maintainer: api@platform-lunar.com
```

# Description

Module that connects to Yieldlab's demand sources

# Test Parameters
```
var adUnits = [
{
code: "test1",
sizes: [[800, 250]]
bids: [{
bidder: "yieldlab",
params: {
placement: "4206978",
accountId: "2358365",
adSize: "800x250"
}
}]
}, {
code: "test2",
sizes: [[1, 1]],
mediaTypes: {
video: {
context: "instream"
}
},
bids: [{
bidder: "yieldlab",
params: {
placementId: "4207034",
accountId: "2358365",
adSize: "1x1"
}
}]
}
];
```
111 changes: 111 additions & 0 deletions test/spec/modules/yieldlabBidAdapter_spec.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
import { expect } from 'chai'
import { spec } from 'modules/yieldlabBidAdapter'
import { newBidder } from 'src/adapters/bidderFactory'

const REQUEST = {
'bidder': 'yieldlab',
'params': {
'placementId': '1111',
'accountId': '2222',
'adSize': '800x250'
},
'bidderRequestId': '143346cf0f1731',
'auctionId': '2e41f65424c87c',
'adUnitCode': 'adunit-code',
'bidId': '2d925f27f5079f',
'sizes': [1, 1]
}

const RESPONSE = {
advertiser: 'yieldlab',
curl: 'https://www.yieldlab.de',
format: 0,
id: 1111,
price: 1
}

describe('yieldlabBidAdapter', () => {
const adapter = newBidder(spec)

describe('inherited functions', () => {
it('exists and is a function', () => {
expect(adapter.callBids).to.exist.and.to.be.a('function')
})
})

describe('isBidRequestValid', () => {
it('should return true when required params found', () => {
const request = {
'params': {
'placementId': '1111',
'accountId': '2222',
'adSize': '800x250'
}
}
expect(spec.isBidRequestValid(request)).to.equal(true)
})

it('should return false when required params are not passed', () => {
expect(spec.isBidRequestValid({})).to.equal(false)
})
})

describe('buildRequests', () => {
const bidRequests = [REQUEST]
const request = spec.buildRequests(bidRequests)

it('sends bid request to ENDPOINT via GET', () => {
expect(request.method).to.equal('GET')
})

it('returns a list of valid requests', () => {
expect(request.validBidRequests).to.eql([REQUEST])
})
})

describe('interpretResponse', () => {
const validRequests = {
validBidRequests: [REQUEST]
}

it('handles nobid responses', () => {
expect(spec.interpretResponse({body: {}}, {validBidRequests: []}).length).to.equal(0)
expect(spec.interpretResponse({body: []}, {validBidRequests: []}).length).to.equal(0)
})

it('should get correct bid response', () => {
const result = spec.interpretResponse({body: [RESPONSE]}, validRequests)

expect(result[0].requestId).to.equal('2d925f27f5079f')
expect(result[0].cpm).to.equal(0.01)
expect(result[0].width).to.equal(800)
expect(result[0].height).to.equal(250)
expect(result[0].creativeId).to.equal('1111')
expect(result[0].dealId).to.equal(undefined)
expect(result[0].currency).to.equal('EUR')
expect(result[0].netRevenue).to.equal(true)
expect(result[0].ttl).to.equal(600)
expect(result[0].referrer).to.equal('')
expect(result[0].ad).to.include('<script src="https://ad.yieldlab.net/d/1111/2222/800x250?ts=')
})

it('should add vastUrl when type is video', () => {
const VIDEO_REQUEST = Object.assign({}, REQUEST, {
'mediaTypes': {
'video': {
'context': 'instream'
}
}
})
const validRequests = {
validBidRequests: [VIDEO_REQUEST]
}
const result = spec.interpretResponse({body: [RESPONSE]}, validRequests)

expect(result[0].requestId).to.equal('2d925f27f5079f')
expect(result[0].cpm).to.equal(0.01)
expect(result[0].mediaType).to.equal('video')
expect(result[0].vastUrl).to.include('https://ad.yieldlab.net/d/1111/2222/1x1?ts=')
})
})
})