Skip to content

Commit

Permalink
Preciso Bid adapter : Initial release (#10138)
Browse files Browse the repository at this point in the history
* New bid adapter : Preciso

* Added deafualt statement in interpretNativeAd

* removed trailing space

* Added Protected Audience API (FLEDGE) support

* updated with getConfig method f pr pulling ortb2 data

* updated the precisoBidAdapter

* updated the test cases

* changed user sync url and also fixed the CORS error

* removed test params from hello_world.html and 204 error fix

* changed responses fields in the precisoBidAdapter.js

* error fix

* removed test params

* reverted the test params

---------

Co-authored-by: Nikhil Gopal Chennissery <[email protected]>
  • Loading branch information
PrecisoSRL and NikhilGopalChennissery committed Aug 23, 2023
1 parent 7edfdff commit 59338f3
Show file tree
Hide file tree
Showing 3 changed files with 378 additions and 0 deletions.
146 changes: 146 additions & 0 deletions modules/precisoBidAdapter.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,146 @@
import { logMessage } from '../src/utils.js';
import {registerBidder} from '../src/adapters/bidderFactory.js';
import { BANNER, NATIVE, VIDEO } from '../src/mediaTypes.js';
import { config } from '../src/config.js';
import { convertOrtbRequestToProprietaryNative } from '../src/native.js';

const BIDDER_CODE = 'preciso';
const AD_URL = 'https://ssp-bidder.mndtrk.com/bid_request/openrtb';
const URL_SYNC = 'https://ck.2trk.info/rtb/user/usersync.aspx?id=preciso_srl';
const SUPPORTED_MEDIA_TYPES = [BANNER, NATIVE, VIDEO];
const GVLID = 874;

export const spec = {
code: BIDDER_CODE,
supportedMediaTypes: SUPPORTED_MEDIA_TYPES,
gvlid: GVLID,

isBidRequestValid: (bid) => {
return Boolean(bid.bidId && bid.params && !isNaN(bid.params.publisherId) && bid.params.host == 'prebid');
},

buildRequests: (validBidRequests = [], bidderRequest) => {
// convert Native ORTB definition to old-style prebid native definition
validBidRequests = convertOrtbRequestToProprietaryNative(validBidRequests);

let winTop = window;
let location;
// TODO: this odd try-catch block was copied in several adapters; it doesn't seem to be correct for cross-origin
try {
location = new URL(bidderRequest.refererInfo.page)
winTop = window.top;
} catch (e) {
location = winTop.location;
logMessage(e);
};
let placements = [];
let imp = [];
let site = {
'domain': location.domain || '',
'page': location || ''
}

let request = {
'id': '123456',
'imp': imp,
'site': site,
'deviceWidth': winTop.screen.width,
'deviceHeight': winTop.screen.height,
'language': (navigator && navigator.language) ? navigator.language : '',
'secure': 1,
'host': location.host,
'page': location.pathname,
'coppa': config.getConfig('coppa') === true ? 1 : 0,
'placements': placements
};
request.language.indexOf('-') != -1 && (request.language = request.language.split('-')[0])
if (bidderRequest) {
if (bidderRequest.uspConsent) {
request.ccpa = bidderRequest.uspConsent;
}
if (bidderRequest.gdprConsent) {
request.gdpr = bidderRequest.gdprConsent
}
if (bidderRequest.gppConsent) {
request.gpp = bidderRequest.gppConsent;
}
}

const len = validBidRequests.length;

for (let i = 0; i < len; i++) {
let bid = validBidRequests[i];
let traff = bid.params.traffic || BANNER
placements.push({
region: bid.params.region,
bidId: bid.bidId,
sizes: bid.mediaTypes && bid.mediaTypes[traff] && bid.mediaTypes[traff].sizes ? bid.mediaTypes[traff].sizes : [],
traffic: traff,
publisherId: bid.params.publisherId
});
imp.push({
id: bid.bidId,
sizes: bid.mediaTypes && bid.mediaTypes[traff] && bid.mediaTypes[traff].sizes ? bid.mediaTypes[traff].sizes : [],
traffic: traff,
publisherId: bid.params.publisherId
})
if (bid.schain) {
placements.schain = bid.schain;
}
}
return {
method: 'POST',
url: AD_URL,
data: request
};
},

interpretResponse: function (serverResponse) {
const response = serverResponse.body

const bids = []

response.seatbid.forEach(seat => {
seat.bid.forEach(bid => {
bids.push({
requestId: bid.impid,
cpm: bid.price,
width: bid.w,
height: bid.h,
creativeId: bid.crid,
ad: bid.adm,
currency: 'USD',
netRevenue: true,
ttl: 300,
meta: {
advertiserDomains: bid.adomain || [],
},
})
})
})

return bids
},

getUserSyncs: (syncOptions, serverResponses = [], gdprConsent = {}, uspConsent = '', gppConsent = '') => {
let syncs = [];
let { gdprApplies, consentString = '' } = gdprConsent;

if (syncOptions.iframeEnabled) {
syncs.push({
type: 'iframe',
url: `${URL_SYNC}&gdpr=${gdprApplies ? 1 : 0}&gdpr_consent=${consentString}&us_privacy=${uspConsent}&t=4`
});
} else {
syncs.push({
type: 'image',
url: `${URL_SYNC}&gdpr=${gdprApplies ? 1 : 0}&gdpr_consent=${consentString}&us_privacy=${uspConsent}&t=2`
});
}

return syncs
}

};

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

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

# Description

Module that connects to preciso' demand sources

# Parameters

| Name | Scope | Description | Example |
| :------------ | :------- | :------------------------ | :------------------- |
| `region` | required (for prebid.js) | region | "prebid-eu" |
| `publisherId` | required (for prebid-server) | partner ID | "1901" |
| `traffic` | optional (for prebid.js) | Configures the mediaType that should be used. Values can be banner, native or video | "banner" |

# Test Parameters
```
var adUnits = [
// Will return static native ad. Assets are stored through user UI for each placement separetly
{
code: 'placementId_0',
mediaTypes: {
native: {}
},
bids: [
{
bidder: 'preciso',
params: {
host: 'prebid',
publisherId: '0',
region: 'prebid-eu',
traffic: 'native'
}
}
]
},
// Will return static test banner
{
code: 'placementId_0',
mediaTypes: {
banner: {
sizes: [[300, 250]],
}
},
bids: [
{
bidder: 'preciso',
params: {
host: 'prebid',
publisherId: '0',
region: 'prebid-eu',
traffic: 'banner'
}
}
]
},
// Will return test vast xml. All video params are stored under placement in publishers UI
{
code: 'placementId_0',
mediaTypes: {
video: {
playerSize: [640, 480],
context: 'instream'
}
},
bids: [
{
bidder: 'preciso',
params: {
host: 'prebid',
publisherId: '0',
region: 'prebid-eu',
traffic: 'video'
}
}
]
}
];
```
148 changes: 148 additions & 0 deletions test/spec/modules/precisoBidAdapter_spec.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,148 @@
import {expect} from 'chai';
import {spec} from '../../../modules/precisoBidAdapter.js';
import { config } from '../../../src/config.js';

const DEFAULT_PRICE = 1
const DEFAULT_CURRENCY = 'USD'
const DEFAULT_BANNER_WIDTH = 300
const DEFAULT_BANNER_HEIGHT = 250
const BIDDER_CODE = 'preciso';

describe('PrecisoAdapter', function () {
let bid = {

bidId: '23fhj33i987f',
bidder: 'preciso',
params: {
host: 'prebid',
sourceid: '0',
publisherId: '0',
traffic: 'banner',
region: 'prebid-eu'

}
};

describe('isBidRequestValid', function () {
it('Should return true if there are bidId, params and sourceid parameters present', function () {
expect(spec.isBidRequestValid(bid)).to.be.true;
});
it('Should return false if at least one of parameters is not present', function () {
delete bid.params.publisherId;
expect(spec.isBidRequestValid(bid)).to.be.false;
});
});

describe('buildRequests', function () {
let serverRequest = spec.buildRequests([bid]);
it('Creates a ServerRequest object with method, URL and data', function () {
expect(serverRequest).to.exist;
expect(serverRequest.method).to.exist;
expect(serverRequest.url).to.exist;
expect(serverRequest.data).to.exist;
});
it('Returns POST method', function () {
expect(serverRequest.method).to.equal('POST');
});
it('Returns valid URL', function () {
expect(serverRequest.url).to.equal('https://ssp-bidder.mndtrk.com/bid_request/openrtb');
});
it('Returns valid data if array of bids is valid', function () {
let data = serverRequest.data;
expect(data).to.be.an('object');
expect(data).to.have.all.keys('id', 'imp', 'site', 'deviceWidth', 'deviceHeight', 'language', 'secure', 'host', 'page', 'placements', 'coppa');
expect(data.deviceWidth).to.be.a('number');
expect(data.deviceHeight).to.be.a('number');
expect(data.coppa).to.be.a('number');
expect(data.language).to.be.a('string');
expect(data.secure).to.be.within(0, 1);
expect(data.host).to.be.a('string');
expect(data.page).to.be.a('string');
let placement = data['placements'][0];
expect(placement).to.have.keys('region', 'bidId', 'traffic', 'sizes', 'publisherId');
expect(placement.bidId).to.equal('23fhj33i987f');
expect(placement.traffic).to.equal('banner');
expect(placement.region).to.equal('prebid-eu');
});
it('Returns empty data if no valid requests are passed', function () {
serverRequest = spec.buildRequests([]);
let data = serverRequest.data;
expect(data.placements).to.be.an('array').that.is.empty;
});
});

describe('with COPPA', function() {
beforeEach(function() {
sinon.stub(config, 'getConfig')
.withArgs('coppa')
.returns(true);
});
afterEach(function() {
config.getConfig.restore();
});

it('should send the Coppa "required" flag set to "1" in the request', function () {
let serverRequest = spec.buildRequests([bid]);
expect(serverRequest.data.coppa).to.equal(1);
});
});

describe('interpretResponse', function () {
it('should get correct bid response', function () {
let response = {
id: 'f6adb85f-4e19-45a0-b41e-2a5b9a48f23a',
seatbid: [
{
bid: [
{
id: '123',
impid: 'b4f290d7-d4ab-4778-ab94-2baf06420b22',
price: DEFAULT_PRICE,
adm: '<b>hi</b>',
cid: 'test_cid',
crid: 'test_banner_crid',
w: DEFAULT_BANNER_WIDTH,
h: DEFAULT_BANNER_HEIGHT,
adomain: [],
}
],
seat: BIDDER_CODE
}
],
}

let expectedResponse = [
{
requestId: 'b4f290d7-d4ab-4778-ab94-2baf06420b22',
cpm: DEFAULT_PRICE,
width: DEFAULT_BANNER_WIDTH,
height: DEFAULT_BANNER_HEIGHT,
creativeId: 'test_banner_crid',
ad: '<b>hi</b>',
currency: DEFAULT_CURRENCY,
netRevenue: true,
ttl: 300,
meta: { advertiserDomains: [] },
}
]
let result = spec.interpretResponse({ body: response })

expect(Object.keys(result[0])).to.have.members(Object.keys(expectedResponse[0]))
})
})
describe('getUserSyncs', function () {
const syncUrl = 'https://ck.2trk.info/rtb/user/usersync.aspx?id=preciso_srl&gdpr=0&gdpr_consent=&us_privacy=&t=4';
const syncOptions = {
iframeEnabled: true
};
let userSync = spec.getUserSyncs(syncOptions);
it('Returns valid URL and type', function () {
expect(userSync).to.be.an('array').with.lengthOf(1);
expect(userSync[0].type).to.exist;
expect(userSync[0].url).to.exist;
expect(userSync).to.deep.equal([
{ type: 'iframe', url: syncUrl }
]);
});
});
});

0 comments on commit 59338f3

Please sign in to comment.