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

Impactify Bid Adapter : add different mediatypes and manage local storage #10601

Merged
merged 23 commits into from
Oct 30, 2023
Merged
Show file tree
Hide file tree
Changes from 19 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
222 changes: 161 additions & 61 deletions modules/impactifyBidAdapter.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,11 @@
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';
import { createEidsArray } from 'modules/userId/eids.js';

const BIDDER_CODE = 'impactify';
const BIDDER_ALIAS = ['imp'];
Expand All @@ -10,35 +14,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;
const STORAGE = getStorageManager({gvlid: GVL_ID, bidderCode: BIDDER_CODE});
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,19 +122,23 @@ 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
let eids = deepAccess(validBidRequests, '0.userIdAsEids');
if (eids && eids.length) {
deepSetValue(request, 'user.ext.eids', eids);
// Set Eids
let bidUserId = deepAccess(validBidRequests, '0.userIdAsEids');
if(bidUserId) {
let eids = createEidsArray(bidUserId);
if (eids.length) {
deepSetValue(request, 'user.ext.eids', eids);
}
}

// Set device/user/site
Expand All @@ -73,7 +147,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 +165,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 +179,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 +231,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 (typeof bid.params.format != 'string' || typeof bid.params.style != 'string' || !bid.params.format || !bid.params.style) {
return false;
}
if (bid.params.format != 'screen' && bid.params.format != 'display') {
if (bid.params.format !== 'screen' && bid.params.format !== 'display') {
return false;
}
if (bid.params.style != 'inline' && bid.params.style != 'impact' && bid.params.style != 'static') {
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 +262,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 +365,7 @@ export const spec = {

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

Expand All @@ -274,7 +374,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 +387,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