-
Notifications
You must be signed in to change notification settings - Fork 2.1k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
3 changed files
with
357 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,136 @@ | ||
import { registerBidder } from '../src/adapters/bidderFactory.js'; | ||
import { BANNER } from '../src/mediaTypes.js'; | ||
import { isEmpty, getAdUnitSizes, parseSizesInput, deepAccess } from '../src/utils.js'; | ||
|
||
const BIDDER_CODE = 'ras'; | ||
const GET_ENDPOINT = (network) => `https://csr.onet.pl/${encodeURIComponent(network)}/csr-006/csr.json?`; | ||
const VERSION = '1.0'; | ||
|
||
function parseParams(params) { | ||
const newParams = {}; | ||
const pageContext = params.pageContext; | ||
if (!pageContext) { | ||
return {}; | ||
} | ||
if (pageContext.dr) { | ||
newParams.dr = pageContext.dr | ||
} | ||
if (pageContext.dv) { | ||
newParams.DV = pageContext.dv | ||
} | ||
if (pageContext.keyWords && Array.isArray(pageContext.keyWords)) { | ||
newParams.kwrd = pageContext.keyWords.join('+') | ||
} | ||
if (pageContext.capping) { | ||
newParams.local_capping = pageContext.capping; | ||
} | ||
if (pageContext.keyValues && typeof pageContext.keyValues === 'object') { | ||
for (const param in pageContext.keyValues) { | ||
if (pageContext.keyValues.hasOwnProperty(param)) { | ||
const kvName = 'kv' + param; | ||
newParams[kvName] = pageContext.keyValues[param] | ||
} | ||
} | ||
} | ||
return newParams; | ||
} | ||
|
||
const buildBid = (ad) => { | ||
if (ad.type === 'empty') { | ||
return null; | ||
} | ||
return { | ||
requestId: ad.id, | ||
cpm: ad.bid_rate ? ad.bid_rate.toFixed(2) : 0, | ||
width: ad.width || 0, | ||
height: ad.height || 0, | ||
ttl: 300, | ||
creativeId: ad.adid ? parseInt(ad.adid.split(',')[2], 10) : 0, | ||
netRevenue: true, | ||
currency: ad.currency || 'USD', | ||
dealId: null, | ||
meta: { | ||
mediaType: BANNER | ||
}, | ||
ad: ad.html || null | ||
}; | ||
}; | ||
|
||
const getContextParams = (bidRequests) => { | ||
const bid = bidRequests[0]; | ||
const { params } = bid; | ||
const requestParams = { | ||
nid: encodeURIComponent(params.network), | ||
site: params.site, | ||
area: params.area, | ||
cre_format: 'html', | ||
systems: 'das', | ||
kvprver: VERSION, | ||
ems_url: 1, | ||
bid_rate: 1, | ||
...parseParams(params) | ||
}; | ||
return Object.keys(requestParams).map((key) => encodeURIComponent(key) + '=' + encodeURIComponent(requestParams[key])).join('&'); | ||
}; | ||
|
||
const getSlots = (bidRequests) => { | ||
let queryString = ''; | ||
const batchSize = bidRequests.length; | ||
for (let i = 0; i < batchSize; i++) { | ||
const adunit = bidRequests[i]; | ||
const { slot } = adunit.params; | ||
const sizes = parseSizesInput(getAdUnitSizes(adunit)).join(','); | ||
queryString += `&slot${i}=${encodeURIComponent(slot)}&id${i}=${encodeURIComponent(adunit.bidId)}&composition${i}=CHILD`; | ||
queryString += sizes.length ? `&iusizes${i}=${encodeURIComponent(sizes)}` : '' | ||
} | ||
return queryString; | ||
}; | ||
|
||
const getGdprParams = (bidderRequest) => { | ||
const gdprApplies = deepAccess(bidderRequest, 'gdprConsent.gdprApplies'); | ||
let consentString = deepAccess(bidderRequest, 'gdprConsent.consentString'); | ||
let queryString = ''; | ||
if (gdprApplies !== undefined) { | ||
queryString += `&gdpr_applies=${encodeURIComponent(gdprApplies)}`; | ||
} | ||
if (consentString !== undefined) { | ||
queryString += `&euconsent=${encodeURIComponent(consentString)}`; | ||
} | ||
return queryString; | ||
}; | ||
|
||
export const spec = { | ||
code: BIDDER_CODE, | ||
supportedMediaTypes: [BANNER], | ||
|
||
isBidRequestValid: function (bidRequest) { | ||
if (!bidRequest || !bidRequest.params || typeof bidRequest.params !== 'object') { | ||
return; | ||
} | ||
const { params } = bidRequest; | ||
return Boolean(params.network && params.site && params.area && params.slot); | ||
}, | ||
|
||
buildRequests: function (bidRequests, bidderRequest) { | ||
const slotsQuery = getSlots(bidRequests); | ||
const contextQuery = getContextParams(bidRequests); | ||
const gdprQuery = getGdprParams(bidderRequest); | ||
const bidIds = bidRequests.map((bid) => ({ slot: bid.params.slot, bidId: bid.bidId })); | ||
const network = bidRequests[0].params.network; | ||
return [{ | ||
method: 'GET', | ||
url: GET_ENDPOINT(network) + contextQuery + slotsQuery + gdprQuery, | ||
bidIds: bidIds | ||
}]; | ||
}, | ||
|
||
interpretResponse: function (serverResponse, bidRequest) { | ||
const response = serverResponse.body; | ||
if (!response || !response.ads || response.ads.length === 0) { | ||
return []; | ||
} | ||
return response.ads.map(buildBid).filter((bid) => !isEmpty(bid)); | ||
} | ||
}; | ||
|
||
registerBidder(spec); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,51 @@ | ||
# Overview | ||
|
||
``` | ||
Module Name: Ringier Axel Springer Bidder Adapter | ||
Module Type: Bidder Adapter | ||
Maintainer: [email protected] | ||
``` | ||
|
||
# Description | ||
|
||
Module that connects to Ringer Axel Springer demand sources. | ||
Only banner format is supported. | ||
|
||
# Test Parameters | ||
```js | ||
var adUnits = [{ | ||
code: 'test-div-ad', | ||
mediaTypes: { | ||
banner: { | ||
sizes: [[300, 250], [300, 600]] | ||
} | ||
}, | ||
bids: [{ | ||
bidder: 'ras', | ||
params: { | ||
network: '4178463', | ||
site: 'test', | ||
area: 'areatest', | ||
slot: 'slot' | ||
} | ||
}] | ||
}]; | ||
``` | ||
|
||
# Parameters | ||
|
||
| Name | Scope | Type | Description | Example | ||
| --- | --- | --- | --- | --- | ||
| network | required | String | Specific identifier provided by RAS | `"4178463"` | ||
| site | required | String | Specific identifier name (case-insensitive) that is associated with this ad unit and provided by RAS | `"example_com"` | ||
| area | required | String | Ad unit category name; only case-insensitive alphanumeric with underscores and hyphens are allowed | `"sport"` | ||
| slot | required | String | Ad unit placement name (case-insensitive) provided by RAS | `"slot"` | ||
| pageContext | optional | Object | Web page context data | `{}` | ||
| pageContext.dr | optional | String | Document referrer URL address | `"https://example.com/"` | ||
| pageContext.du | optional | String | Document URL address | `"https://example.com/sport/football/article.html?id=932016a5-02fc-4d5c-b643-fafc2f270f06"` | ||
| pageContext.dv | optional | String | Document virtual address as slash-separated path that may consist of any number of parts (case-insensitive alphanumeric with underscores and hyphens); first part should be the same as `site` value and second as `area` value; next parts may reflect website navigation | `"example_com/sport/football"` | ||
| pageContext.keyWords | optional | String[] | List of keywords associated with this ad unit; only case-insensitive alphanumeric with underscores and hyphens are allowed | `["euro", "lewandowski"]` | ||
| pageContext.keyValues | optional | Object | Key-values associated with this ad unit (case-insensitive); following characters are not allowed in the values: `" ' = ! + # * ~ ; ^ ( ) < > [ ] & @` | `{}` | ||
| pageContext.keyValues.pos | optional | Number | Ad unit position (integer greather than zero) | `1` | ||
| pageContext.keyValues.ci | optional | String | Content unique identifier | `"932016a5-02fc-4d5c-b643-fafc2f270f06"` | ||
| pageContext.keyValues.adunit | optional | String | Ad unit name | `"example_com/sport"` |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,170 @@ | ||
import { expect } from 'chai'; | ||
import { spec } from 'modules/rasBidAdapter.js'; | ||
import { newBidder } from 'src/adapters/bidderFactory.js'; | ||
|
||
const CSR_ENDPOINT = 'https://csr.onet.pl/1746213/csr-006/csr.json?'; | ||
|
||
describe('rasBidAdapter', function () { | ||
const adapter = newBidder(spec); | ||
|
||
describe('inherited functions', function () { | ||
it('exists and is a function', function () { | ||
expect(adapter.callBids).to.exist.and.to.be.a('function'); | ||
}); | ||
}); | ||
|
||
describe('isBidRequestValid', function () { | ||
it('should return true when required params found', function () { | ||
const bid = { | ||
sizes: [[300, 250], [300, 600]], | ||
bidder: 'ras', | ||
params: { | ||
slot: 'slot', | ||
area: 'NOWASG', | ||
site: 'GLOWNA', | ||
network: '1746213' | ||
} | ||
}; | ||
expect(spec.isBidRequestValid(bid)).to.equal(true); | ||
}); | ||
|
||
it('should return false when required params not found', function () { | ||
const failBid = { | ||
sizes: [[300, 250], [300, 300]], | ||
bidder: 'ras', | ||
params: { | ||
site: 'GLOWNA', | ||
network: '1746213' | ||
} | ||
}; | ||
expect(spec.isBidRequestValid(failBid)).to.equal(false); | ||
}); | ||
|
||
it('should return nothing when bid request is malformed', function () { | ||
const failBid = { | ||
sizes: [[300, 250], [300, 300]], | ||
bidder: 'ras', | ||
}; | ||
expect(spec.isBidRequestValid(failBid)).to.equal(undefined); | ||
}); | ||
}); | ||
|
||
describe('buildRequests', function () { | ||
const bid = { | ||
sizes: [[300, 250], [300, 600]], | ||
bidder: 'ras', | ||
bidId: 1, | ||
params: { | ||
slot: 'test', | ||
area: 'NOWASG', | ||
site: 'GLOWNA', | ||
network: '1746213' | ||
} | ||
}; | ||
const bid2 = { | ||
sizes: [[750, 300]], | ||
bidder: 'ras', | ||
bidId: 2, | ||
params: { | ||
slot: 'test2', | ||
area: 'NOWASG', | ||
site: 'GLOWNA', | ||
network: '1746213' | ||
} | ||
}; | ||
|
||
it('should parse bids to request', function () { | ||
const requests = spec.buildRequests([bid], { | ||
'gdprConsent': { | ||
'gdprApplies': true, | ||
'consentString': 'some-consent-string' | ||
} | ||
}); | ||
expect(requests[0].url).to.have.string(CSR_ENDPOINT); | ||
expect(requests[0].url).to.have.string('slot0=test'); | ||
expect(requests[0].url).to.have.string('id0=1'); | ||
expect(requests[0].url).to.have.string('nid=1746213'); | ||
expect(requests[0].url).to.have.string('site=GLOWNA'); | ||
expect(requests[0].url).to.have.string('area=NOWASG'); | ||
expect(requests[0].url).to.have.string('cre_format=html'); | ||
expect(requests[0].url).to.have.string('systems=das'); | ||
expect(requests[0].url).to.have.string('ems_url=1'); | ||
expect(requests[0].url).to.have.string('bid_rate=1'); | ||
expect(requests[0].url).to.have.string('gdpr_applies=true'); | ||
expect(requests[0].url).to.have.string('euconsent=some-consent-string'); | ||
}); | ||
it('should return empty consent string when undefined', function () { | ||
const requests = spec.buildRequests([bid]); | ||
const gdpr = requests[0].url.search('gdpr_applies'); | ||
const euconsent = requests[0].url.search('euconsent='); | ||
expect(gdpr).to.equal(-1); | ||
expect(euconsent).to.equal(-1); | ||
}); | ||
it('should parse bids to request from pageContext', function () { | ||
const bidCopy = { ...bid }; | ||
bidCopy.params = { ...bid.params, pageContext: { 'dr': 'test.pl', keyValues: { seg_ab: 10 } } }; | ||
const requests = spec.buildRequests([bidCopy, bid2]); | ||
expect(requests[0].url).to.have.string(CSR_ENDPOINT); | ||
expect(requests[0].url).to.have.string('slot0=test'); | ||
expect(requests[0].url).to.have.string('slot0=test'); | ||
expect(requests[0].url).to.have.string('id0=1'); | ||
expect(requests[0].url).to.have.string('iusizes0=300x250%2C300x600'); | ||
expect(requests[0].url).to.have.string('slot1=test2'); | ||
expect(requests[0].url).to.have.string('id1=2'); | ||
expect(requests[0].url).to.have.string('iusizes1=750x300'); | ||
expect(requests[0].url).to.have.string('nid=1746213'); | ||
expect(requests[0].url).to.have.string('site=GLOWNA'); | ||
expect(requests[0].url).to.have.string('area=NOWASG'); | ||
expect(requests[0].url).to.have.string('cre_format=html'); | ||
expect(requests[0].url).to.have.string('systems=das'); | ||
expect(requests[0].url).to.have.string('ems_url=1'); | ||
expect(requests[0].url).to.have.string('bid_rate=1'); | ||
expect(requests[0].url).to.have.string('dr=test.pl'); | ||
}); | ||
}); | ||
|
||
describe('interpretResponse', function () { | ||
const response = { | ||
'adsCheck': 'ok', | ||
'geoloc': {}, | ||
'ir': '92effd60-0c84-4dac-817e-763ea7b8ac65', | ||
'ads': [ | ||
{ | ||
'id': 'flat-belkagorna', | ||
'slot': 'flat-belkagorna', | ||
'prio': 10, | ||
'type': 'html', | ||
'bid_rate': 0.321123, | ||
'adid': 'das,50463,152276', | ||
'id_3': '12734', | ||
'html': '<script type=\"text/javascript\">test</script>' | ||
} | ||
], | ||
'iv': '202003191334467636346500' | ||
}; | ||
|
||
it('should get correct bid response', function () { | ||
const resp = spec.interpretResponse({ body: response }, { bidIds: [{ slot: 'flat-belkagorna', bidId: 1 }] }); | ||
expect(resp[0]).to.have.all.keys('cpm', 'currency', 'netRevenue', 'requestId', 'ttl', 'width', 'height', 'creativeId', 'dealId', 'ad', 'meta'); | ||
expect(resp.length).to.equal(1); | ||
}); | ||
|
||
it('should handle empty ad', function () { | ||
let res = { | ||
'ads': [{ | ||
type: 'empty' | ||
}] | ||
}; | ||
const resp = spec.interpretResponse({ body: res }, {}); | ||
expect(resp).to.deep.equal([]); | ||
}); | ||
|
||
it('should handle empty server response', function () { | ||
let res = { | ||
'ads': [] | ||
}; | ||
const resp = spec.interpretResponse({ body: res }, {}); | ||
expect(resp).to.deep.equal([]); | ||
}); | ||
}); | ||
}); |