Skip to content

Commit

Permalink
Impactify Bid Adapter : add different mediatypes and manage local sto…
Browse files Browse the repository at this point in the history
…rage (#10601)

* Remove use of local storage

As requested, we remove the use of local storage.
#8689

* Update impactifyBidAdapter.js

* Add differents mediatypes to Impactify bidder

* Add differents mediatypes to Impactify bidder

* Add differents mediatypes to Impactify bidder

* Add format parameter for banner

* add getFloor

* add getFloor

* add getFloor

* add parsing of local storage

* delete unused var

* fix spacing with import

* Add local storage key management

* Adjustments

* Fix eids object

* Fix eids object

* Fix eids object

* Fix tests

---------

Co-authored-by: Thomas De Stefano <[email protected]>
Co-authored-by: Pang Ronnie <[email protected]>
  • Loading branch information
3 people authored Oct 30, 2023
1 parent 0e61728 commit 5bea607
Show file tree
Hide file tree
Showing 3 changed files with 342 additions and 81 deletions.
212 changes: 154 additions & 58 deletions modules/impactifyBidAdapter.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
import {deepAccess, deepSetValue, generateUUID} from '../src/utils.js';
import {registerBidder} from '../src/adapters/bidderFactory.js';
import {config} from '../src/config.js';
import {ajax} from '../src/ajax.js';
'use strict';

import { deepAccess, deepSetValue, generateUUID } from '../src/utils.js';
import { registerBidder } from '../src/adapters/bidderFactory.js';
import { config } from '../src/config.js';
import { ajax } from '../src/ajax.js';
import { getStorageManager } from '../src/storageManager.js';

const BIDDER_CODE = 'impactify';
const BIDDER_ALIAS = ['imp'];
Expand All @@ -10,35 +13,101 @@ const DEFAULT_VIDEO_WIDTH = 640;
const DEFAULT_VIDEO_HEIGHT = 360;
const ORIGIN = 'https://sonic.impactify.media';
const LOGGER_URI = 'https://logger.impactify.media';
const AUCTIONURI = '/bidder';
const COOKIESYNCURI = '/static/cookie_sync.html';
const GVLID = 606;
const GETCONFIG = config.getConfig;

const getDeviceType = () => {
// OpenRTB Device type
if ((/ipad|android 3.0|xoom|sch-i800|playbook|tablet|kindle/i.test(navigator.userAgent.toLowerCase()))) {
return 5;
}
if ((/iphone|ipod|android|blackberry|opera|mini|windows\sce|palm|smartphone|iemobile/i.test(navigator.userAgent.toLowerCase()))) {
return 4;
}
return 2;
};
const AUCTION_URI = '/bidder';
const COOKIE_SYNC_URI = '/static/cookie_sync.html';
const GVL_ID = 606;
const GET_CONFIG = config.getConfig;
export const STORAGE = getStorageManager({gvlid: GVL_ID, bidderCode: BIDDER_CODE});
export const STORAGE_KEY = '_im_str'

/**
* Helpers object
* @type {{getExtParamsFromBid(*): {impactify: {appId}}, createOrtbImpVideoObj(*): {context: string, playerSize: [number,number], id: string, mimes: [string]}, getDeviceType(): (number), createOrtbImpBannerObj(*, *): {format: [], id: string}}}
*/
const helpers = {
getExtParamsFromBid(bid) {
let ext = {
impactify: {
appId: bid.params.appId
},
};

const getFloor = (bid) => {
const floorInfo = bid.getFloor({
currency: DEFAULT_CURRENCY,
mediaType: '*',
size: '*'
});
if (typeof floorInfo === 'object' && floorInfo.currency === DEFAULT_CURRENCY && !isNaN(parseFloat(floorInfo.floor))) {
return parseFloat(floorInfo.floor);
if (typeof bid.params.format == 'string') {
ext.impactify.format = bid.params.format;
}

if (typeof bid.params.style == 'string') {
ext.impactify.style = bid.params.style;
}

if (typeof bid.params.container == 'string') {
ext.impactify.container = bid.params.container;
}

if (typeof bid.params.size == 'string') {
ext.impactify.size = bid.params.size;
}

return ext;
},

getDeviceType() {
// OpenRTB Device type
if ((/ipad|android 3.0|xoom|sch-i800|playbook|tablet|kindle/i.test(navigator.userAgent.toLowerCase()))) {
return 5;
}
if ((/iphone|ipod|android|blackberry|opera|mini|windows\sce|palm|smartphone|iemobile/i.test(navigator.userAgent.toLowerCase()))) {
return 4;
}
return 2;
},

createOrtbImpBannerObj(bid, size) {
let sizes = size.split('x');

return {
id: 'banner-' + bid.bidId,
format: [{
w: parseInt(sizes[0]),
h: parseInt(sizes[1])
}]
}
},

createOrtbImpVideoObj(bid) {
return {
id: 'video-' + bid.bidId,
playerSize: [DEFAULT_VIDEO_WIDTH, DEFAULT_VIDEO_HEIGHT],
context: 'outstream',
mimes: ['video/mp4'],
}
},

getFloor(bid) {
const floorInfo = bid.getFloor({
currency: DEFAULT_CURRENCY,
mediaType: '*',
size: '*'
});
if (typeof floorInfo === 'object' && floorInfo.currency === DEFAULT_CURRENCY && !isNaN(parseFloat(floorInfo.floor))) {
return parseFloat(floorInfo.floor);
}
return null;
},

getImStrFromLocalStorage() {
return STORAGE.localStorageIsEnabled(false) ? STORAGE.getDataFromLocalStorage(STORAGE_KEY, false) : '';
}
return null;

}

const createOpenRtbRequest = (validBidRequests, bidderRequest) => {
/**
* Create an OpenRTB formated object from prebid payload
* @param validBidRequests
* @param bidderRequest
* @returns {{cur: string[], validBidRequests, id, source: {tid}, imp: *[]}}
*/
function createOpenRtbRequest(validBidRequests, bidderRequest) {
// Create request and set imp bids inside
let request = {
id: bidderRequest.bidderRequestId,
Expand All @@ -52,16 +121,17 @@ const createOpenRtbRequest = (validBidRequests, bidderRequest) => {
const queryString = window.location.search;
const urlParams = new URLSearchParams(queryString);
const checkPrebid = urlParams.get('_checkPrebid');
// Force impactify debugging parameter

// Force impactify debugging parameter if present
if (checkPrebid != null) {
request.test = Number(checkPrebid);
}

// Set Schain in request
// Set SChain in request
let schain = deepAccess(validBidRequests, '0.schain');
if (schain) request.source.ext = { schain: schain };

// Set eids
// Set Eids
let eids = deepAccess(validBidRequests, '0.userIdAsEids');
if (eids && eids.length) {
deepSetValue(request, 'user.ext.eids', eids);
Expand All @@ -73,7 +143,7 @@ const createOpenRtbRequest = (validBidRequests, bidderRequest) => {
request.device = {
w: window.innerWidth,
h: window.innerHeight,
devicetype: getDeviceType(),
devicetype: helpers.getDeviceType(),
ua: navigator.userAgent,
js: 1,
dnt: (navigator.doNotTrack == 'yes' || navigator.doNotTrack == '1' || navigator.msDoNotTrack == '1') ? 1 : 0,
Expand All @@ -91,9 +161,10 @@ const createOpenRtbRequest = (validBidRequests, bidderRequest) => {

if (bidderRequest.uspConsent) {
deepSetValue(request, 'regs.ext.us_privacy', bidderRequest.uspConsent);
this.syncStore.uspConsent = bidderRequest.uspConsent;
}

if (GETCONFIG('coppa') == true) deepSetValue(request, 'regs.coppa', 1);
if (GET_CONFIG('coppa') == true) deepSetValue(request, 'regs.coppa', 1);

if (bidderRequest.uspConsent) {
deepSetValue(request, 'regs.ext.us_privacy', bidderRequest.uspConsent);
Expand All @@ -104,42 +175,50 @@ const createOpenRtbRequest = (validBidRequests, bidderRequest) => {

// Create imps with bids
validBidRequests.forEach((bid) => {
let bannerObj = deepAccess(bid.mediaTypes, `banner`);
let videoObj = deepAccess(bid.mediaTypes, `video`);

let imp = {
id: bid.bidId,
bidfloor: bid.params.bidfloor ? bid.params.bidfloor : 0,
ext: {
impactify: {
appId: bid.params.appId,
format: bid.params.format,
style: bid.params.style
},
},
video: {
playerSize: [DEFAULT_VIDEO_WIDTH, DEFAULT_VIDEO_HEIGHT],
context: 'outstream',
mimes: ['video/mp4'],
},
ext: helpers.getExtParamsFromBid(bid)
};
if (bid.params.container) {
imp.ext.impactify.container = bid.params.container;

if (videoObj) {
imp.video = {
...helpers.createOrtbImpVideoObj(bid)
}
}

if (bannerObj && typeof imp.ext.impactify.size == 'string') {
imp.banner = {
...helpers.createOrtbImpBannerObj(bid, imp.ext.impactify.size)
}
}

if (typeof bid.getFloor === 'function') {
const floor = getFloor(bid);
const floor = helpers.getFloor(bid);
if (floor) {
imp.bidfloor = floor;
}
}

request.imp.push(imp);
});

return request;
};
}

/**
* Export BidderSpec type object and register it to Prebid
* @type {{supportedMediaTypes: string[], interpretResponse: ((function(ServerResponse, *): Bid[])|*), code: string, aliases: string[], getUserSyncs: ((function(SyncOptions, ServerResponse[], *, *): UserSync[])|*), buildRequests: (function(*, *): {method: string, data: string, url}), onTimeout: (function(*): boolean), gvlid: number, isBidRequestValid: ((function(BidRequest): (boolean))|*), onBidWon: (function(*): boolean)}}
*/
export const spec = {
code: BIDDER_CODE,
gvlid: GVLID,
gvlid: GVL_ID,
supportedMediaTypes: ['video', 'banner'],
aliases: BIDDER_ALIAS,
storageAllowed: true,

/**
* Determines whether or not the given bid request is valid.
Expand All @@ -148,13 +227,21 @@ export const spec = {
* @return boolean True if this is a valid bid, and false otherwise.
*/
isBidRequestValid: function (bid) {
if (!bid.params.appId || typeof bid.params.appId != 'string' || !bid.params.format || typeof bid.params.format != 'string' || !bid.params.style || typeof bid.params.style != 'string') {
let isBanner = deepAccess(bid.mediaTypes, `banner`);

if (typeof bid.params.appId != 'string' || !bid.params.appId) {
return false;
}
if (bid.params.format != 'screen' && bid.params.format != 'display') {
if (typeof bid.params.format != 'string' || typeof bid.params.style != 'string' || !bid.params.format || !bid.params.style) {
return false;
}
if (bid.params.style != 'inline' && bid.params.style != 'impact' && bid.params.style != 'static') {
if (bid.params.format !== 'screen' && bid.params.format !== 'display') {
return false;
}
if (bid.params.style !== 'inline' && bid.params.style !== 'impact' && bid.params.style !== 'static') {
return false;
}
if (isBanner && (typeof bid.params.size != 'string' || !bid.params.size.includes('x') || bid.params.size.split('x').length != 2)) {
return false;
}

Expand All @@ -171,11 +258,20 @@ export const spec = {
buildRequests: function (validBidRequests, bidderRequest) {
// Create a clean openRTB request
let request = createOpenRtbRequest(validBidRequests, bidderRequest);
const imStr = helpers.getImStrFromLocalStorage();
const options = {}

if (imStr) {
options.customHeaders = {
'x-impact': imStr
};
}

return {
method: 'POST',
url: ORIGIN + AUCTIONURI,
url: ORIGIN + AUCTION_URI,
data: JSON.stringify(request),
options
};
},

Expand Down Expand Up @@ -265,7 +361,7 @@ export const spec = {

return [{
type: 'iframe',
url: ORIGIN + COOKIESYNCURI + params
url: ORIGIN + COOKIE_SYNC_URI + params
}];
},

Expand All @@ -274,7 +370,7 @@ export const spec = {
* @param {Bid} The bid that won the auction
*/
onBidWon: function(bid) {
ajax(`${LOGGER_URI}/log/bidder/won`, null, JSON.stringify(bid), {
ajax(`${LOGGER_URI}/prebid/won`, null, JSON.stringify(bid), {
method: 'POST',
contentType: 'application/json'
});
Expand All @@ -287,7 +383,7 @@ export const spec = {
* @param {data} Containing timeout specific data
*/
onTimeout: function(data) {
ajax(`${LOGGER_URI}/log/bidder/timeout`, null, JSON.stringify(data[0]), {
ajax(`${LOGGER_URI}/prebid/timeout`, null, JSON.stringify(data[0]), {
method: 'POST',
contentType: 'application/json'
});
Expand Down
38 changes: 33 additions & 5 deletions modules/impactifyBidAdapter.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,14 +10,22 @@ Maintainer: [email protected]

Module that connects to the Impactify solution.
The impactify bidder need 3 parameters:
- appId : This is your unique publisher identifier
- format : This is the ad format needed, can be : screen or display
- style : This is the ad style needed, can be : inline, impact or static
- appId : This is your unique publisher identifier
- format : This is the ad format needed, can be : screen or display (Only for video media type)
- style : This is the ad style needed, can be : inline, impact or static (Only for video media type)

Note : Impactify adapter need storage access to work properly (Do not forget to set storageAllowed to true).

# Test Parameters
```
var adUnits = [{
code: 'your-slot-div-id', // This is your slot div id
pbjs.bidderSettings = {
impactify: {
storageAllowed: true // Mandatory
}
};
var adUnitsVideo = [{
code: 'your-slot-div-id-video', // This is your slot div id
mediaTypes: {
video: {
context: 'outstream'
Expand All @@ -32,4 +40,24 @@ The impactify bidder need 3 parameters:
}
}]
}];
var adUnitsBanner = [{
code: 'your-slot-div-id-banner', // This is your slot div id
mediaTypes: {
banner: {
sizes: [
[728, 90]
]
}
},
bids: [{
bidder: 'impactify',
params: {
appId: 'example.com',
format: 'display',
size: '728x90',
style: 'static'
}
}]
}];
```
Loading

0 comments on commit 5bea607

Please sign in to comment.