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

Reset Digital Bid Adapter: add new bid adapter #6710

Merged
merged 7 commits into from
Jun 10, 2021
Merged
Show file tree
Hide file tree
Changes from 4 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
144 changes: 144 additions & 0 deletions modules/resetdigitalBidAdapter.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,144 @@

import * as utils from '../src/utils.js';
import { config } from '../src/config.js';
import {registerBidder} from '../src/adapters/bidderFactory.js';
const BIDDER_CODE = 'resetdigital';

function getDomain () {
evanmsmrtb marked this conversation as resolved.
Show resolved Hide resolved
if (!utils.inIframe()) {
return window.location.hostname
}
let origins = window.document.location.ancestorOrigins
if (origins && origins.length > 0) {
return origins[origins.length - 1]
}
}

export const spec = {
code: BIDDER_CODE,
supportedMediaTypes: [ 'banner', 'video' ],
isBidRequestValid: function(bid) {
return (bid.params.pubId !== null || bid.params.zoneId !== null);
evanmsmrtb marked this conversation as resolved.
Show resolved Hide resolved
},
buildRequests: function(validBidRequests, bidderRequest) {
let stack = (bidderRequest.refererInfo &&
bidderRequest.refererInfo.stack ? bidderRequest.refererInfo
evanmsmrtb marked this conversation as resolved.
Show resolved Hide resolved
: [])

let spb = (config.getConfig('userSync') && config.getConfig('userSync').syncsPerBidder)
? config.getConfig('userSync').syncsPerBidder : 5

const payload = {
start_time: utils.timestamp(),
language: window.navigator.userLanguage || window.navigator.language,
site: {
domain: getDomain(),
iframe: !bidderRequest.refererInfo.reachedTop,
url: stack && stack.length > 0 ? [stack.length - 1] : null,
https: (window.location.protocol === 'https:'),
referrer: bidderRequest.refererInfo.referer
},
imps: [],
user_ids: validBidRequests[0].userId,
sync_limit: spb
};

if (bidderRequest && bidderRequest.gdprConsent) {
payload.gdpr = {
applies: bidderRequest.gdprConsent.gdprApplies,
consent: bidderRequest.gdprConsent.consentString
};
}

for (let x = 0; x < validBidRequests.length; x++) {
let req = validBidRequests[x]

payload.imps.push({
pub_id: req.params.pubId,
zone_id: req.params.zoneId,
bid_id: req.bidId,
imp_id: req.transactionId,
sizes: req.sizes,
force_bid: req.params.forceBid,
media_types: utils.deepAccess(req, 'mediaTypes')
});
}

let params = validBidRequests[0].params
let url = params.endpoint ? params.endpoint : '//hb.vhsrv.com'
return {
method: 'POST',
url: url,
data: JSON.stringify(payload)
};
},
interpretResponse: function(serverResponse, bidRequest) {
const bidResponses = [];
if (!serverResponse || !serverResponse.body) {
return bidResponses
}

let res = serverResponse.body;
if (!res.bids || !res.bids.length) {
return []
}

for (let x = 0; x < serverResponse.body.bids.length; x++) {
let bid = serverResponse.body.bids[x]

bidResponses.push({
requestId: bid.bid_id,
cpm: bid.cpm,
width: bid.w,
height: bid.h,
ad: bid.html,
vastUrl: bid.vast_url,
vastXml: bid.vast_xml,
mediaType: bid.html ? 'banner' : 'video',
ttl: 120,
creativeId: bid.crid,
dealId: bid.deal_id,
netRevenue: true,
currency: 'USD'
})
}

return bidResponses;
},
getUserSyncs: function(syncOptions, serverResponses, gdprConsent) {
const syncs = []

if (!serverResponses.length || !serverResponses[0].body) {
return syncs
}

let pixels = serverResponses[0].body.pixels
if (!pixels || !pixels.length) {
return syncs
}

let gdprParams = null
if (gdprConsent) {
if (typeof gdprConsent.gdprApplies === 'boolean') {
gdprParams = `gdpr=${Number(gdprConsent.gdprApplies)}&gdpr_consent=${gdprConsent.consentString}`
} else {
gdprParams = `gdpr_consent=${gdprConsent.consentString}`
}
}

for (let x = 0; x < pixels.length; x++) {
let pixel = pixels[x]

if ((pixel.type === 'iframe' && syncOptions.iframeEnabled) ||
(pixel.type === 'image' && syncOptions.pixelEnabled)) {
if (gdprParams && gdprParams.length) {
pixel = (pixel.indexOf('?') === -1 ? '?' : '&') + gdprParams
}
syncs.push(pixel)
}
}
return syncs;
}
};

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

```
Module Name: ResetDigital Bidder Adapter
Module Type: Bidder Adapter
Maintainer: [email protected]
```

# Description

Prebid adapter for Reset Digital. Requires approval and account setup.
Video is supported but requires a publisher supplied renderer at this time.

# Test Parameters

## Web
```
var adUnits = [
{
code: 'your-div',
mediaTypes: {
banner: {
sizes: [[300,250]]
}
},
bids: [
{
bidder: "resetdigital",
params: {
pubId: "your-pub-id",
forceBid: true
}
}
]
}
];
```
158 changes: 158 additions & 0 deletions test/spec/modules/resetdigitalBidAdapter_spec.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,158 @@
import { expect } from 'chai'
import { spec, _getPlatform } from 'modules/resetdigitalBidAdapter.js'
import { newBidder } from 'src/adapters/bidderFactory.js'

const br = {
body: {
bids: [{
bid_id: '123',
cpm: 1.23,
w: 300,
h: 250,
html: '<b>deadbeef</b>',
crid: 'crid'
}],
pixels: [
{ type: 'image', url: 'https://reset.com/image' },
{ type: 'iframe', url: 'https://reset.com/iframe' }
]
}
}

const vr = {
body: {
bids: [{
bid_id: 'abc',
cpm: 2.34,
w: 640,
h: 480,
vast_url: 'https://demo.tremorvideo.com/proddev/vast/vast_inline_nonlinear.xml',
crid: 'video_crid'
}],
pixels: [
{ type: 'image', url: 'https://reset.com/image' },
{ type: 'iframe', url: 'https://reset.com/iframe' }
]
}
}

describe('resetdigitalBidAdapter', function () {
const adapter = newBidder(spec)

let bannerRequest = {
bidId: '123',
transactionId: '456',
mediaTypes: {
banner: {
sizes: [[300, 250], [300, 600]]
}
},
params: {
pubId: '12345'
}
}

let videoRequest = {
bidId: 'abc',
transactionId: 'def',
mediaTypes: {
video: {
playerDimension: [640, 480]
}
},
params: {
pubId: 'CK6gUYp58EGopLJnUvM2'
}
}

describe('codes', function () {
it('should return a bidder code of resetdigital', function () {
expect(spec.code).to.equal('resetdigital')
})
})

describe('isBidRequestValid', function () {
it('should return true if all params present', function () {
expect(spec.isBidRequestValid(bannerRequest)).to.be.true
})

it('should return false if zone id and pub id missing', function () {
expect(spec.isBidRequestValid(Object.assign(bannerRequest, { params: { pubId: null, zoneId: null } }))).to.be.false
})
})

describe('buildRequests', function () {
let req = spec.buildRequests([ bannerRequest ], { refererInfo: { } })
let rdata

it('should return request object', function () {
expect(req).to.not.be.null
})

it('should build request data', function () {
expect(req.data).to.not.be.null
})

it('should include one request', function () {
rdata = JSON.parse(req.data)
expect(rdata.imps.length).to.equal(1)
})

it('should include all publisher params', function () {
expect(rdata.imps[0].zone_id !== null).to.be.true
})

it('should include media types', function () {
expect(rdata.imps[0].media_types !== null).to.be.true
})
})

describe('interpretResponse', function () {
it('should form compliant banner bid object response', function () {
let ir = spec.interpretResponse(br, bannerRequest)

expect(ir.length).to.equal(1)

let en = ir[0]

expect(en.requestId != null &&
en.cpm != null && typeof en.cpm === 'number' &&
en.width != null && typeof en.width === 'number' &&
en.height != null && typeof en.height === 'number' &&
en.ad != null &&
en.creativeId != null
).to.be.true
})
it('should form compliant video object response', function () {
let ir = spec.interpretResponse(vr, videoRequest)

expect(ir.length).to.equal(1)

let en = ir[0]

expect(en.requestId != null &&
en.cpm != null && typeof en.cpm === 'number' &&
en.width != null && typeof en.width === 'number' &&
en.height != null && typeof en.height === 'number' &&
(en.vastUrl != null || en.vastXml != null) &&
en.creativeId != null
).to.be.true
})
})

describe('getUserSyncs', function () {
it('should return iframe sync', function () {
let sync = spec.getUserSyncs({ iframeEnabled: true }, [br])
expect(sync.length).to.equal(1)
expect(sync[0].type === 'iframe')
expect(typeof sync[0].url === 'string')
})

it('should return pixel sync', function () {
let sync = spec.getUserSyncs({ pixelEnabled: true }, [br])
expect(sync.length).to.equal(1)
expect(sync[0].type === 'image')
expect(typeof sync[0].url === 'string')
})
})
})