Skip to content

Commit

Permalink
Sharethrough Bid Adapter: Add First Party Data and Outstream Support (#…
Browse files Browse the repository at this point in the history
…7857)

* [PGE-178206477] Ingest First Party Data and send to Ad Server

* Use nullish

* [PGE-178206515] Allow outstream video

* Fix ortb2 usage in specs
  • Loading branch information
Mathieu Pheulpin authored Jan 6, 2022
1 parent 62a6f59 commit a5c29b5
Show file tree
Hide file tree
Showing 2 changed files with 103 additions and 23 deletions.
29 changes: 12 additions & 17 deletions modules/sharethroughBidAdapter.js
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
import { generateUUID, deepAccess, inIframe } from '../src/utils.js';
import { deepAccess, generateUUID, inIframe } from '../src/utils.js';
import { registerBidder } from '../src/adapters/bidderFactory.js';
import { config } from '../src/config.js';
import { BANNER, VIDEO } from '../src/mediaTypes.js';
import { createEidsArray } from './userId/eids.js';

const VERSION = '4.0.1';
const VERSION = '4.1.0';
const BIDDER_CODE = 'sharethrough';
const SUPPLY_ID = 'WYu2BXv1';

Expand All @@ -23,6 +23,7 @@ export const sharethroughAdapterSpec = {

buildRequests: (bidRequests, bidderRequest) => {
const timeout = config.getConfig('bidderTimeout');
const firstPartyData = config.getConfig('ortb2') || {};

const nonHttp = sharethroughInternal.getProtocol().indexOf('http') < 0;
const secure = nonHttp || (sharethroughInternal.getProtocol().indexOf('https') > -1);
Expand All @@ -35,12 +36,8 @@ export const sharethroughAdapterSpec = {
site: {
domain: window.location.hostname,
page: window.location.href,
ref: bidderRequest.refererInfo ? bidderRequest.refererInfo.referer || null : null,
},
user: {
ext: {
eids: userIdAsEids(bidRequests[0]),
},
ref: deepAccess(bidderRequest, 'refererInfo.referer'),
...firstPartyData.site,
},
device: {
ua: navigator.userAgent,
Expand All @@ -66,6 +63,10 @@ export const sharethroughAdapterSpec = {
test: 0,
};

req.user = nullish(firstPartyData.user, {});
if (!req.user.ext) req.user.ext = {};
req.user.ext.eids = userIdAsEids(bidRequests[0]);

if (bidderRequest.gdprConsent) {
const gdprApplies = bidderRequest.gdprConsent.gdprApplies === true;
req.regs.ext.gdpr = gdprApplies ? 1 : 0;
Expand All @@ -86,15 +87,9 @@ export const sharethroughAdapterSpec = {
impression.ext = { gpid: gpid };
}

// if request is for video, we only support instream
if (bidReq.mediaTypes && bidReq.mediaTypes.video && bidReq.mediaTypes.video.context === 'outstream') {
// return null so we can easily remove this imp from the array of imps that we send to adserver
return null;
}

if (bidReq.mediaTypes && bidReq.mediaTypes.video) {
const videoRequest = bidReq.mediaTypes.video;
const videoRequest = deepAccess(bidReq, 'mediaTypes.video');

if (videoRequest) {
// default playerSize, only change this if we know width and height are properly defined in the request
let [w, h] = [640, 360];
if (videoRequest.playerSize && videoRequest.playerSize[0] && videoRequest.playerSize[1]) {
Expand All @@ -117,9 +112,9 @@ export const sharethroughAdapterSpec = {
startdelay: nullish(videoRequest.startdelay, 0),
skipmin: nullish(videoRequest.skipmin, 0),
skipafter: nullish(videoRequest.skipafter, 0),
placement: videoRequest.context === 'instream' ? 1 : +deepAccess(videoRequest, 'placement', 4),
};

if (videoRequest.placement) impression.video.placement = videoRequest.placement;
if (videoRequest.delivery) impression.video.delivery = videoRequest.delivery;
if (videoRequest.companiontype) impression.video.companiontype = videoRequest.companiontype;
if (videoRequest.companionad) impression.video.companionad = videoRequest.companionad;
Expand Down
97 changes: 91 additions & 6 deletions test/spec/modules/sharethroughBidAdapter_spec.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { expect } from 'chai';
import * as sinon from 'sinon';
import { sharethroughAdapterSpec, sharethroughInternal } from 'modules/sharethroughBidAdapter.js';
import { newBidder } from 'src/adapters/bidderFactory.js';
import { config } from 'src/config';
Expand Down Expand Up @@ -157,7 +158,6 @@ describe('sharethrough adapter spec', function () {
startdelay: 42,
skipmin: 10,
skipafter: 20,
placement: 1,
delivery: 1,
companiontype: 'companion type',
companionad: 'companion ad',
Expand Down Expand Up @@ -205,6 +205,7 @@ describe('sharethrough adapter spec', function () {
expect(openRtbReq.cur).to.deep.equal(['USD']);
expect(openRtbReq.tmax).to.equal(242);

expect(Object.keys(openRtbReq.site)).to.have.length(3);
expect(openRtbReq.site.domain).not.to.be.undefined;
expect(openRtbReq.site.page).not.to.be.undefined;
expect(openRtbReq.site.ref).to.equal('https://referer.com');
Expand Down Expand Up @@ -256,6 +257,17 @@ describe('sharethrough adapter spec', function () {
});
});

describe('no referer provided', () => {
beforeEach(() => {
bidderRequest = {};
});

it('should set referer to undefined', () => {
const openRtbReq = spec.buildRequests(bidRequests, bidderRequest)[0].data;
expect(openRtbReq.site.ref).to.be.undefined;
});
});

describe('regulation', () => {
describe('gdpr', () => {
it('should populate request accordingly when gdpr applies', () => {
Expand Down Expand Up @@ -419,17 +431,90 @@ describe('sharethrough adapter spec', function () {
expect(videoImp.startdelay).to.equal(0);
expect(videoImp.skipmin).to.equal(0);
expect(videoImp.skipafter).to.equal(0);
expect(videoImp.placement).to.be.undefined;
expect(videoImp.placement).to.equal(1);
expect(videoImp.delivery).to.be.undefined;
expect(videoImp.companiontype).to.be.undefined;
expect(videoImp.companionad).to.be.undefined;
});

it('should not return a video impression if context is outstream', () => {
bidRequests[1].mediaTypes.video.context = 'outstream';
const builtRequest = spec.buildRequests(bidRequests, bidderRequest)[1];
describe('outstream', () => {
it('should use placement value if provided', () => {
bidRequests[1].mediaTypes.video.context = 'outstream';
bidRequests[1].mediaTypes.video.placement = 3;

const builtRequest = spec.buildRequests(bidRequests, bidderRequest)[1];
const videoImp = builtRequest.data.imp[0].video;

expect(videoImp.placement).to.equal(3);
});

it('should default placement to 4 if not provided', () => {
bidRequests[1].mediaTypes.video.context = 'outstream';

const builtRequest = spec.buildRequests(bidRequests, bidderRequest)[1];
const videoImp = builtRequest.data.imp[0].video;

expect(videoImp.placement).to.equal(4);
});
});
});

describe('first party data', () => {
const firstPartyData = {
site: {
name: 'example',
keywords: 'power tools, drills',
search: 'drill',
content: {
userrating: '4',
},
ext: {
data: {
pageType: 'article',
category: 'repair',
},
},
},
user: {
yob: 1985,
gender: 'm',
ext: {
data: {
registered: true,
interests: ['cars'],
},
},
},
};

let configStub;

beforeEach(() => {
configStub = sinon.stub(config, 'getConfig');
configStub.withArgs('ortb2').returns(firstPartyData);
});

afterEach(() => {
configStub.restore();
});

it('should include first party data in open rtb request, site section', () => {
const openRtbReq = spec.buildRequests(bidRequests, bidderRequest)[0].data;

expect(openRtbReq.site.name).to.equal(firstPartyData.site.name);
expect(openRtbReq.site.keywords).to.equal(firstPartyData.site.keywords);
expect(openRtbReq.site.search).to.equal(firstPartyData.site.search);
expect(openRtbReq.site.content).to.deep.equal(firstPartyData.site.content);
expect(openRtbReq.site.ext).to.deep.equal(firstPartyData.site.ext);
});

it('should include first party data in open rtb request, user section', () => {
const openRtbReq = spec.buildRequests(bidRequests, bidderRequest)[0].data;

expect(builtRequest).to.be.undefined;
expect(openRtbReq.user.yob).to.equal(firstPartyData.user.yob);
expect(openRtbReq.user.gender).to.equal(firstPartyData.user.gender);
expect(openRtbReq.user.ext.data).to.deep.equal(firstPartyData.user.ext.data);
expect(openRtbReq.user.ext.eids).not.to.be.undefined;
});
});
});
Expand Down

0 comments on commit a5c29b5

Please sign in to comment.