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

Integral Ad Science RTD module: initial release #6816

Merged
merged 10 commits into from
Jun 8, 2021
1 change: 1 addition & 0 deletions modules/.submodules.json
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@
"dgkeywordRtdProvider",
"geoedgeRtdProvider",
"haloRtdProvider",
"iasRtdProvider",
"jwplayerRtdProvider",
"permutiveRtdProvider",
"reconciliationRtdProvider",
Expand Down
122 changes: 122 additions & 0 deletions modules/iasRtdProvider.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@
import { submodule } from '../src/hook.js';
import { getGlobal } from '../src/prebidGlobal.js';
import * as utils from '../src/utils.js';
import { ajaxBuilder } from '../src/ajax.js';

/** @type {string} */
const MODULE_NAME = 'realTimeData';
const SUBMODULE_NAME = 'ias';

/**
* Module init
* @param {Object} provider
* @param {Object} userConsent
* @return {boolean}
*/
export function init(config, userConsent) {
return true;
}

function stringifySlotSizes(sizes) {
let result = '';
if (utils.isArray(sizes)) {
result = sizes.reduce((acc, size) => {
acc.push(size.join('.'));
return acc;
}, []);
result = '[' + result.join(',') + ']';
}
return result;
}

function stringifySlot(bidRequest) {
const id = bidRequest.code;
const ss = stringifySlotSizes(bidRequest.sizes);
const p = bidRequest.bids[0].params.adUnitPath;
const slot = { id, ss, p };
const keyValues = utils.getKeys(slot).map(function (key) {
return [key, slot[key]].join(':');
});
return '{' + keyValues.join(',') + '}';
}

function stringifyWindowSize() {
return [window.innerWidth || -1, window.innerHeight || -1].join('.');
}

function stringifyScreenSize() {
return [(window.screen && window.screen.width) || -1, (window.screen && window.screen.height) || -1].join('.');
}

function getPageLevelKeywords(response) {
let result = {};
shallowMerge(result, response.brandSafety);
result.fr = response.fr;
result.custom = response.custom;
return result;
}

function shallowMerge(dest, src) {
utils.getKeys(src).reduce((dest, srcKey) => {
raguilar-ias marked this conversation as resolved.
Show resolved Hide resolved
dest[srcKey] = src[srcKey];
return dest;
}, dest);
}

function getBidRequestData(reqBidsConfigObj, callback, config) {
const adUnits = reqBidsConfigObj.adUnits || getGlobal().adUnits;

let isFinish = false;

const IAS_HOST = 'https://pixel.adsafeprotected.com/services/pub';
const { pubId } = config.params;
const anId = pubId;
let queries = [];
queries.push(['anId', anId]);

queries = queries.concat(adUnits.reduce(function (acc, request) {
acc.push(['slot', stringifySlot(request)]);
return acc;
}, []));

queries.push(['wr', stringifyWindowSize()]);
queries.push(['sr', stringifyScreenSize()]);
queries.push(['url', encodeURIComponent(window.location.href)]);

const queryString = encodeURI(queries.map(qs => qs.join('=')).join('&'));

const ajax = ajaxBuilder();

ajax(`${IAS_HOST}?${queryString}`, {
success: function (response, request) {
if (!isFinish) {
if (request.status === 200) {
const iasResponse = JSON.parse(response);
adUnits.forEach(adUnit => {
adUnit.bids.forEach(bid => {
const rtd = bid.rtd || {};
const iasRtd = {};
iasRtd[SUBMODULE_NAME] = Object.assign({}, rtd[SUBMODULE_NAME], getPageLevelKeywords(iasResponse));
bid.rtd = Object.assign({}, rtd, iasRtd);
});
});
}
isFinish = true;
}
callback();
},
error: function () {
utils.logError('failed to retrieve targeting information');
callback();
}
});
}

/** @type {RtdSubmodule} */
export const iasSubModule = {
name: SUBMODULE_NAME,
init: init,
getBidRequestData: getBidRequestData
};

submodule(MODULE_NAME, iasSubModule);
9 changes: 9 additions & 0 deletions modules/iasRtdProvider.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
# Overview

Module Name: Integral Ad Science(IAS) Rtd Provider
Module Type: Rtd Provider
Maintainer: [email protected]

# Description

RTD provider for Integral Ad Science(IAS) Contact [email protected] for information.
74 changes: 74 additions & 0 deletions test/spec/modules/iasRtdProvider_spec.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
import { iasSubModule } from 'modules/iasRtdProvider.js';
import { expect } from 'chai';

describe('iasRtdProvider is a RTD provider that', function () {
it('has the correct module name', function () {
expect(iasSubModule.name).to.equal('ias');
});
describe('has a method `init` that', function () {
it('exists', function () {
expect(iasSubModule.init).to.be.a('function');
});
it('returns true', function () {
expect(iasSubModule.init()).to.equal(true);
});
});
describe('has a method `getBidRequestData` that', function () {
const callback = sinon.spy();
const config = {
name: 'ias',
waitForIt: true,
params: {
pubId: 1234
}
};
it('exists', function () {
expect(iasSubModule.getBidRequestData).to.be.a('function');
});
it('verify config params', function () {
expect(config.name).to.not.be.undefined;
expect(config.name).to.equal('ias');
expect(config.params.pubId).to.not.be.undefined;
expect(config.params).to.have.property('pubId');
});
it('invoke method', function () {
iasSubModule.getBidRequestData({ adUnits: adUnits }, callback, config);
expect(adUnits).to.length(2);
expect(callback.calledOnce).to.be.false;
});
});
});

const adUnits = [
{
code: 'one-div-id',
mediaTypes: {
banner: {
sizes: [[970, 250], [728, 90], [1000, 90]]
}
},
sizes: [[970, 250], [728, 90], [1000, 90]],
bids: [
{
bidder: 'ias',
params: {
pubId: '1234',
adUnitPath: '/a/b/c'
}
}]
},
{
code: 'two-div-id',
mediaTypes: {
banner: { sizes: [[300, 250], [300, 600]] }
},
sizes: [[300, 250], [300, 600]],
bids: [
{
bidder: 'ias',
params: {
pubId: '1234',
adUnitPath: '/d/e/f'
}
}]
}];