Skip to content

Commit

Permalink
AdagioBidAdapter: change outstream renderer (#10035)
Browse files Browse the repository at this point in the history
  • Loading branch information
osazos authored Jun 2, 2023
1 parent d71feb8 commit 01381e1
Show file tree
Hide file tree
Showing 2 changed files with 132 additions and 41 deletions.
110 changes: 86 additions & 24 deletions modules/adagioBidAdapter.js
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,11 @@ const ADAGIO_TAG_URL = 'https://script.4dex.io/localstore.js';
const ADAGIO_LOCALSTORAGE_KEY = 'adagioScript';
const GVLID = 617;
export const storage = getStorageManager({bidderCode: BIDDER_CODE});
export const RENDERER_URL = 'https://script.4dex.io/outstream-player.js';

const BB_PUBLICATION = 'adagio';
const BB_RENDERER_DEFAULT = 'renderer';
export const BB_RENDERER_URL = `https://${BB_PUBLICATION}.bbvms.com/r/$RENDERER.js`;

const MAX_SESS_DURATION = 30 * 60 * 1000;
const ADAGIO_PUBKEY = 'AL16XT44Sfp+8SHVF1UdC7hydPSMVLMhsYknKDdwqq+0ToDSJrP0+Qh0ki9JJI2uYm/6VEYo8TJED9WfMkiJ4vf02CW3RvSWwc35bif2SK1L8Nn/GfFYr/2/GG/Rm0vUsv+vBHky6nuuYls20Og0HDhMgaOlXoQ/cxMuiy5QSktp';
const ADAGIO_PUBKEY_E = 65537;
Expand All @@ -62,7 +66,7 @@ export const ORTB_VIDEO_PARAMS = {
'startdelay': (value) => isInteger(value),
'placement': (value) => isInteger(value),
'linearity': (value) => isInteger(value),
'skip': (value) => isInteger(value),
'skip': (value) => [1, 0].includes(value),
'skipmin': (value) => isInteger(value),
'skipafter': (value) => isInteger(value),
'sequence': (value) => isInteger(value),
Expand Down Expand Up @@ -443,16 +447,6 @@ function _buildVideoBidRequest(bidRequest) {
});
}

function _renderer(bid) {
bid.renderer.push(() => {
if (typeof window.ADAGIO.outstreamPlayer === 'function') {
window.ADAGIO.outstreamPlayer(bid);
} else {
logError(`${LOG_PREFIX} Adagio outstream player is not defined`);
}
});
}

function _parseNativeBidResponse(bid) {
if (!bid.admNative || !Array.isArray(bid.admNative.assets)) {
logError(`${LOG_PREFIX} Invalid native response`);
Expand Down Expand Up @@ -881,6 +875,77 @@ function storeRequestInAdagioNS(bidRequest) {
};
}

// See https://support.bluebillywig.com/developers/vast-renderer/
const OUTSTREAM_RENDERER = {
bootstrapPlayer: function(bid) {
const rendererCode = bid.outstreamRendererCode;

const config = {
code: bid.adUnitCode,
};

if (bid.vastXml) {
config.vastXml = bid.vastXml;
} else if (bid.vastUrl) {
config.vastUrl = bid.vastUrl;
}

if (!bid.vastXml && !bid.vastUrl) {
logError(`${LOG_PREFIX} no vastXml or vastUrl on bid`);
return;
}

if (!window.bluebillywig || !window.bluebillywig.renderers || !window.bluebillywig.renderers.length) {
logError(`${LOG_PREFIX} no BlueBillywig renderers found!`);
return;
}

const rendererId = this.getRendererId(BB_PUBLICATION, rendererCode);

const override = {}
if (bid.skipOffset) {
override.skipOffset = bid.skipOffset.toString()
}

const renderer = window.bluebillywig.renderers.find(bbr => bbr._id === rendererId);
if (!renderer) {
logError(`${LOG_PREFIX} couldn't find a renderer with ID ${rendererId}`);
return;
}

const el = document.getElementById(bid.adUnitCode);

renderer.bootstrap(config, el, override);
},
newRenderer: function(adUnitCode, rendererCode) {
const rendererUrl = BB_RENDERER_URL.replace('$RENDERER', rendererCode);

const renderer = Renderer.install({
url: rendererUrl,
loaded: false,
adUnitCode
});

try {
renderer.setRender(this.outstreamRender);
} catch (err) {
logError(`${LOG_PREFIX} error trying to setRender`, err);
}

return renderer;
},
outstreamRender: function(bid) {
bid.renderer.push(() => {
OUTSTREAM_RENDERER.bootstrapPlayer(bid)
});
},
getRendererId: function(publication, renderer) {
// By convention, the RENDERER_ID is always the publication name (adagio) and the ad unit code (eg. renderer)
// joined together by a dash. It's used to identify the correct renderer instance on the page in case there's multiple.
return `${publication}-${renderer}`;
}
};

export const spec = {
code: BIDDER_CODE,
gvlid: GVLID,
Expand Down Expand Up @@ -1110,21 +1175,18 @@ export const spec = {
const mediaTypeContext = deepAccess(bidReq, 'mediaTypes.video.context');
// Adagio SSP returns a `vastXml` only. No `vastUrl` nor `videoCacheKey`.
if (!bidObj.vastUrl && bidObj.vastXml) {
bidObj.vastUrl = 'data:text/xml;charset=utf-8;base64,' + btoa(bidObj.vastXml.replace(/\\"/g, '"'));
bidObj.vastUrl = 'data:text/xml;charset=utf-8;base64,' + window.btoa(bidObj.vastXml.replace(/\\"/g, '"'));
}

if (mediaTypeContext === OUTSTREAM) {
bidObj.renderer = Renderer.install({
id: bidObj.requestId,
adUnitCode: bidObj.adUnitCode,
url: bidObj.urlRenderer || RENDERER_URL,
config: {
...deepAccess(bidReq, 'mediaTypes.video'),
...deepAccess(bidObj, 'outstream', {})
}
});

bidObj.renderer.setRender(_renderer);
bidObj.outstreamRendererCode = deepAccess(bidReq, 'params.rendererCode', BB_RENDERER_DEFAULT)

if (deepAccess(bidReq, 'mediaTypes.video.skip')) {
const skipOffset = deepAccess(bidReq, 'mediaTypes.video.skipafter', 5) // default 5s.
bidObj.skipOffset = skipOffset
}

bidObj.renderer = OUTSTREAM_RENDERER.newRenderer(bidObj.adUnitCode, bidObj.outstreamRendererCode);
}
}

Expand Down
63 changes: 46 additions & 17 deletions test/spec/modules/adagioBidAdapter_spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import {
spec,
ENDPOINT,
VERSION,
RENDERER_URL,
BB_RENDERER_URL,
GlobalExchange
} from '../../../modules/adagioBidAdapter.js';
import { loadExternalScript } from '../../../src/adloader.js';
Expand Down Expand Up @@ -76,6 +76,7 @@ describe('Adagio bid adapter', () => {
let adagioMock;
let utilsMock;
let sandbox;
let fakeRenderer;

const fixtures = {
getElementById(width, height, x, y) {
Expand Down Expand Up @@ -1000,42 +1001,70 @@ describe('Adagio bid adapter', () => {
context: 'outstream',
playerSize: [[300, 250]],
mimes: ['video/mp4'],
skip: true
skip: 1,
skipafter: 3
};

const serverResponseWithOutstream = utils.deepClone(serverResponse);
serverResponseWithOutstream.body.bids[0].vastXml = '<VAST version="4.0"><Ad></Ad></VAST>';
serverResponseWithOutstream.body.bids[0].mediaType = 'video';
serverResponseWithOutstream.body.bids[0].outstream = {
bvwUrl: 'https://foo.baz',
impUrl: 'https://foo.bar'
};

it('should set a renderer in video outstream context', function() {
const defaultRendererUrl = BB_RENDERER_URL.replace('$RENDERER', 'renderer');

it('should set related properties for video outstream context', function() {
const bidResponse = spec.interpretResponse(serverResponseWithOutstream, bidRequestWithOutstream)[0];
expect(bidResponse).to.have.any.keys('outstream', 'renderer', 'mediaType');
expect(bidResponse).to.have.any.keys('renderer', 'mediaType');
expect(bidResponse.renderer).to.be.a('object');
expect(bidResponse.renderer.url).to.equal(RENDERER_URL);
expect(bidResponse.renderer.config.bvwUrl).to.be.ok;
expect(bidResponse.renderer.config.impUrl).to.be.ok;
expect(bidResponse.renderer.url).to.equal(defaultRendererUrl);
expect(bidResponse.renderer.loaded).to.not.be.ok;
expect(bidResponse.width).to.equal(300);
expect(bidResponse.height).to.equal(250);
expect(bidResponse.vastUrl).to.match(/^data:text\/xml;/)
});

it('should execute Adagio outstreamPlayer if defined', function() {
window.ADAGIO.outstreamPlayer = sinon.stub();
it('should execute Blue Billywig VAST Renderer bootstrap if defined', function() {
window.bluebillywig = {
renderers: [{ bootstrap: sinon.stub(), _id: 'adagio-renderer' }]
};

const bidResponse = spec.interpretResponse(serverResponseWithOutstream, bidRequestWithOutstream)[0];
executeRenderer(bidResponse.renderer, bidResponse)
sinon.assert.calledOnce(window.bluebillywig.renderers[0].bootstrap);

delete window.bluebillywig;
});

it('Should logError if response does not have a vastXml or vastUrl', function() {
utilsMock.expects('logError').withExactArgs('Adagio: no vastXml or vastUrl on bid').once();

const localServerResponseWithOutstream = utils.deepClone(serverResponse);
localServerResponseWithOutstream.body.bids[0].mediaType = 'video';

const bidResponse = spec.interpretResponse(localServerResponseWithOutstream, bidRequestWithOutstream)[0];
executeRenderer(bidResponse.renderer, bidResponse)

utilsMock.verify();
})

it('should logError if Blue Billywig API is not defined', function() {
utilsMock.expects('logError').withExactArgs('Adagio: no BlueBillywig renderers found!').once();

const bidResponse = spec.interpretResponse(serverResponseWithOutstream, bidRequestWithOutstream)[0];
executeRenderer(bidResponse.renderer, bidResponse)
sinon.assert.calledOnce(window.ADAGIO.outstreamPlayer);
delete window.ADAGIO.outstreamPlayer;

utilsMock.verify();
});

it('should logError if Adagio outstreamPlayer is not defined', function() {
it('should logError if correct renderer is not defined', function() {
window.bluebillywig = { renderers: [ { _id: 'adagio-another_renderer' } ] };

utilsMock.expects('logError').withExactArgs('Adagio: couldn\'t find a renderer with ID adagio-renderer').once();

const bidResponse = spec.interpretResponse(serverResponseWithOutstream, bidRequestWithOutstream)[0];
executeRenderer(bidResponse.renderer, bidResponse)
utilsMock.expects('logError').withExactArgs('Adagio: Adagio outstream player is not defined').once();

delete window.bluebillywig;
utilsMock.verify();
});
});

Expand Down

0 comments on commit 01381e1

Please sign in to comment.