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

update AMX RTB bid adapter #5605

Merged
merged 1 commit into from
Sep 9, 2020
Merged
Show file tree
Hide file tree
Changes from all 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
74 changes: 72 additions & 2 deletions modules/amxBidAdapter.js
Original file line number Diff line number Diff line change
@@ -1,14 +1,18 @@
import { registerBidder } from '../src/adapters/bidderFactory.js';
import { BANNER, VIDEO } from '../src/mediaTypes.js';
import { parseUrl, deepAccess, _each, formatQS, getUniqueIdentifierStr, triggerPixel } from '../src/utils.js';
import { config } from '../src/config.js';
import { getStorageManager } from '../src/storageManager.js';

const BIDDER_CODE = 'amx';
const storage = getStorageManager(737, BIDDER_CODE);
const SIMPLE_TLD_TEST = /\.co\.\w{2,4}$/;
const DEFAULT_ENDPOINT = 'https://prebid.a-mo.net/a/c';
const VERSION = 'pba1.0';
const VERSION = 'pba1.2';
const xmlDTDRxp = /^\s*<\?xml[^\?]+\?>/;
const VAST_RXP = /^\s*<\??(?:vast|xml)/i;
const TRACKING_ENDPOINT = 'https://1x1.a-mo.net/hbx/';
const AMUID_KEY = '__amuidpb';

const getLocation = (request) =>
parseUrl(deepAccess(request, 'refererInfo.canonicalUrl', location.href))
Expand Down Expand Up @@ -47,6 +51,22 @@ function getID(loc) {

const enc = encodeURIComponent;

function getUIDSafe() {
try {
return storage.getDataFromLocalStorage(AMUID_KEY)
} catch (e) {
return null
}
}

function setUIDSafe(uid) {
try {
storage.setDataInLocalStorage(AMUID_KEY, uid)
} catch (e) {
// do nothing
}
}

function nestedQs (qsData) {
const out = [];
Object.keys(qsData || {}).forEach((key) => {
Expand Down Expand Up @@ -77,9 +97,20 @@ function convertRequest(bid) {
const av = isVideoBid || size[1] > 100;
const tid = deepAccess(bid, 'params.tagId')

const au = bid.params != null && typeof bid.params.adUnitId === 'string'
? bid.params.adUnitId : bid.adUnitCode;

const multiSizes = [
bid.sizes,
deepAccess(bid, `mediaTypes.${BANNER}.sizes`, []) || [],
deepAccess(bid, `mediaTypes.${VIDEO}.sizes`, []) || [],
]

const params = {
au,
av,
vr: isVideoBid,
ms: multiSizes,
aw: size[0],
ah: size[1],
tf: 0,
Expand Down Expand Up @@ -159,6 +190,16 @@ function resolveSize(bid, request, bidId) {
return [bidRequest.aw, bidRequest.ah];
}

function values(source) {
if (Object.values != null) {
return Object.values(source)
}

return Object.keys(source).map((key) => {
return source[key]
});
}

export const spec = {
code: BIDDER_CODE,
supportedMediaTypes: [BANNER, VIDEO],
Expand All @@ -173,11 +214,19 @@ export const spec = {
const loc = getLocation(bidderRequest);
const tagId = deepAccess(bidRequests[0], 'params.tagId', null);
const testMode = deepAccess(bidRequests[0], 'params.testMode', 0);
const fbid = bidRequests[0] != null ? bidRequests[0] : {
bidderRequestsCount: 0,
bidderWinsCount: 0,
bidRequestsCount: 0
}

const payload = {
a: bidderRequest.auctionId,
B: 0,
b: loc.host,
brc: fbid.bidderRequestsCount || 0,
bwc: fbid.bidderWinsCount || 0,
trc: fbid.bidRequestsCount || 0,
tm: testMode,
V: '$prebid.version$',
i: (testMode && tagId != null) ? tagId : getID(loc),
Expand All @@ -187,15 +236,32 @@ export const spec = {
st: 'prebid',
h: screen.height,
w: screen.width,
gs: deepAccess(bidderRequest, 'gdprConsent.gdprApplies', '0'),
gs: deepAccess(bidderRequest, 'gdprConsent.gdprApplies', ''),
gc: deepAccess(bidderRequest, 'gdprConsent.consentString', ''),
u: deepAccess(bidderRequest, 'refererInfo.canonicalUrl', loc.href),
do: loc.host,
re: deepAccess(bidderRequest, 'refererInfo.referer'),
am: getUIDSafe(),
usp: bidderRequest.uspConsent || '1---',
smt: 1,
d: '',
m: createBidMap(bidRequests),
cpp: config.getConfig('coppa') ? 1 : 0,
fpd: config.getConfig('fpd'),
eids: values(bidRequests.reduce((all, bid) => {
// we only want unique ones in here
if (bid == null || bid.userIdAsEids == null) {
return all
}

_each(bid.userIdAsEids, (value) => {
if (value == null) {
return;
}
all[value.source] = value
});
return all;
}, {})),
};

return {
Expand Down Expand Up @@ -234,6 +300,10 @@ export const spec = {
return [];
}

if (response.am && typeof response.am === 'string') {
setUIDSafe(response.am);
}

return flatMap(Object.keys(response.r), (bidID) => {
return flatMap(response.r[bidID], (siteBid) =>
siteBid.b.map((bid) => {
Expand Down
1 change: 1 addition & 0 deletions modules/amxBidAdapter.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ This module connects web publishers to AMX RTB video and display demand.
| --- | -------- | ------- | ----------- |
| `testMode` | no | `true` | this will activate test mode / 100% fill with sample ads |
| `tagId` | no | `"cHJlYmlkLm9yZw"` | can be used for more specific targeting of inventory. Your account manager will provide this ID if needed |
| `adUnitId` | no | `"sticky_banner"` | optional. To override the bid.adUnitCode provided by prebid. For use in ad-unit level reporting |

# Test Parameters

Expand Down
91 changes: 90 additions & 1 deletion test/spec/modules/amxBidAdapter_spec.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
import * as utils from 'src/utils.js';
import { createEidsArray } from 'modules/userId/eids.js';
import { expect } from 'chai';
import { spec } from 'modules/amxBidAdapter.js';
import { BANNER, VIDEO } from 'src/mediaTypes.js';
import { config } from 'src/config.js';

const sampleRequestId = '82c91e127a9b93e';
const sampleDisplayAd = (additionalImpressions) => `<script src='https://assets.a-mo.net/tmode.v1.js'></script>${additionalImpressions}`;
Expand All @@ -14,6 +16,28 @@ const sampleVideoAd = (addlImpression) => `
const embeddedTrackingPixel = `https://1x1.a-mo.net/hbx/g_impression?A=sample&B=20903`;
const sampleNurl = 'https://example.exchange/nurl';

const sampleFPD = {
context: {
keywords: 'sample keywords',
data: {
pageType: 'article'
}
},
user: {
gender: 'O',
yob: 1982,
}
};

const stubConfig = (withStub) => {
const stub = sinon.stub(config, 'getConfig').callsFake(
(arg) => arg === 'fpd' ? sampleFPD : null
)

withStub();
stub.restore();
};

const sampleBidderRequest = {
gdprConsent: {
gdprApplies: true,
Expand Down Expand Up @@ -165,12 +189,59 @@ describe('AmxBidAdapter', () => {
expect(data.tm).to.equal(true);
});

it('handles referer data and GDPR, USP Consent', () => {
it('handles referer data and GDPR, USP Consent, COPPA', () => {
const { data } = spec.buildRequests([sampleBidRequestBase], sampleBidderRequest);
delete data.m; // don't deal with "m" in this test
expect(data.gs).to.equal(sampleBidderRequest.gdprConsent.gdprApplies)
expect(data.gc).to.equal(sampleBidderRequest.gdprConsent.consentString)
expect(data.usp).to.equal(sampleBidderRequest.uspConsent)
expect(data.cpp).to.equal(0)
});

it('will forward bid request count & wins count data', () => {
const bidderRequestsCount = Math.floor(Math.random() * 100)
const bidderWinsCount = Math.floor(Math.random() * 100)
const { data } = spec.buildRequests([{
...sampleBidRequestBase,
bidderRequestsCount,
bidderWinsCount
}], sampleBidderRequest);

expect(data.brc).to.equal(bidderRequestsCount)
expect(data.bwc).to.equal(bidderWinsCount)
expect(data.trc).to.equal(0)
});
it('will forward first-party data', () => {
stubConfig(() => {
const { data } = spec.buildRequests([sampleBidRequestBase], sampleBidderRequest);
expect(data.fpd).to.deep.equal(sampleFPD)
});
});

it('will collect & forward RTI user IDs', () => {
const randomRTI = `greatRTI${Math.floor(Math.random() * 100)}`
const userId = {
britepoolid: 'sample-britepool',
criteoId: 'sample-criteo',
digitrustid: {data: {id: 'sample-digitrust'}},
id5id: 'sample-id5',
idl_env: 'sample-liveramp',
lipb: {lipbid: 'sample-liveintent'},
netId: 'sample-netid',
parrableId: { eid: 'sample-parrable' },
pubcid: 'sample-pubcid',
[randomRTI]: 'sample-unknown',
tdid: 'sample-ttd',
};

const eids = createEidsArray(userId);
const bid = {
...sampleBidRequestBase,
userIdAsEids: eids
};

const { data } = spec.buildRequests([bid, bid], sampleBidderRequest);
expect(data.eids).to.deep.equal(eids)
});

it('can build a banner request', () => {
Expand All @@ -188,6 +259,12 @@ describe('AmxBidAdapter', () => {
expect(Object.keys(data.m).length).to.equal(2);
expect(data.m[sampleRequestId]).to.deep.equal({
av: true,
au: 'div-gpt-ad-example',
ms: [
[[320, 50]],
[[300, 250]],
[]
],
aw: 300,
ah: 250,
tf: 0,
Expand All @@ -196,6 +273,12 @@ describe('AmxBidAdapter', () => {
expect(data.m[sampleRequestId + '_2']).to.deep.equal({
av: true,
aw: 300,
au: 'div-gpt-ad-example',
ms: [
[[320, 50]],
[[300, 250]],
[]
],
i: 'example',
ah: 250,
tf: 0,
Expand All @@ -207,6 +290,12 @@ describe('AmxBidAdapter', () => {
const { data } = spec.buildRequests([sampleBidRequestVideo], sampleBidderRequest);
expect(Object.keys(data.m).length).to.equal(1);
expect(data.m[sampleRequestId + '_video']).to.deep.equal({
au: 'div-gpt-ad-example',
ms: [
[[300, 150]],
[],
[[360, 250]]
],
av: true,
aw: 360,
ah: 250,
Expand Down