Skip to content

Instantly share code, notes, and snippets.

@walia
Created April 21, 2020 19:05
Show Gist options
  • Save walia/b625b2dcb6c37dc5f4d2194fb8bed6d9 to your computer and use it in GitHub Desktop.
Save walia/b625b2dcb6c37dc5f4d2194fb8bed6d9 to your computer and use it in GitHub Desktop.
ix adapter work
'use strict';
////////////////////////////////////////////////////////////////////////////////
// Dependencies ////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////
var Inspector = require('../../../libs/external/schema-inspector.js');
////////////////////////////////////////////////////////////////////////////////
// Main ////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////
/* =============================================================================
* STEP 0 | Config Validation
* -----------------------------------------------------------------------------
* This file contains the necessary validation for the partner configuration.
* This validation will be performed on the partner specific configuration object
* that is passed into the wrapper. The wrapper uses an outside library called
* schema-insepctor to perform the validation. Information about it can be found here:
* https://atinux.fr/schema-inspector/.
*/
function partnerValidator(configs) {
var result = Inspector.validate({
type: 'object',
properties: {
partnerId: {
type: 'string',
minLength: 1
},
xSlots: {
type: 'object',
properties: {
'*': {
type: 'object',
properties: {
sizes: {
type: 'array',
minLength: 1,
items: {
type: 'array',
exactLength: 2,
items: {
type: 'integer'
}
}
}
}
}
}
}
}
}, configs);
if (!result.valid) {
return result.format();
}
return null;
}
module.exports = partnerValidator;
'use strict';
////////////////////////////////////////////////////////////////////////////////
// Dependencies ////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////
var Browser = require('browser.js');
var Classify = require('classify.js');
var Constants = require('constants.js');
var Partner = require('partner.js');
var Size = require('size.js');
var SpaceCamp = require('space-camp.js');
var System = require('system.js');
var Network = require('network.js');
var Utilities = require('utilities.js');
var ComplianceService;
var RenderService;
//? if (DEBUG) {
var ConfigValidators = require('config-validators.js');
var PartnerSpecificValidator = require('vroom-htb-validator.js');
var Scribe = require('scribe.js');
var Whoopsie = require('whoopsie.js');
//? }
////////////////////////////////////////////////////////////////////////////////
// Main ////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////
/**
* Partner module template
*
* @class
*/
function VroomHtb(configs) {
/* =====================================
* Data
* ---------------------------------- */
/* Private
* ---------------------------------- */
/**
* Reference to the partner base class.
*
* @private {object}
*/
var __baseClass;
/**
* Profile for this partner.
*
* @private {object}
*/
var __profile;
/* =====================================
* Functions
* ---------------------------------- */
/* Utilities
* ---------------------------------- */
/**
* Generates the request URL and query data to the endpoint for the xSlots
* in the given returnParcels.
*
* @param {object[]} returnParcels
*
* @return {object}
*/
function __generateRequestObj(returnParcels) {
/* =============================================================================
* STEP 2 | Generate Request URL
* -----------------------------------------------------------------------------
* /
/* ---------------------- PUT CODE HERE ------------------------------------ */
console.log("😭😭😭😭");
console.log(configs);
var callbackId = System.generateUniqueId();
var queryObj = {
slots: returnParcels.map(function (parcel) {
return {
name: parcel.xSlotName,
sizes: parcel.xSlotRef.sizes,
partnerId: configs.partnerId,
callbackId: callbackId
};
}),
meta: {
adapterVersion: __profile.version,
pageUrl: Browser.getPageUrl(),
screen: Size.arrayToString([Browser.getScreenWidth(), Browser.getScreenHeight()]),
debug: true
}
};
console.log(queryObj);
console.log('🦠🦠🦠');
/* Change this to your bidder endpoint. */
// var baseUrl = Browser.getProtocol() + '//someAdapterEndpoint.com/bid';
// var baseUrl = 'https://bids.concert.io/bids/index';
var baseUrl = 'http://localhost:8080/bids/index';
/* ------------------------ Get consent information -------------------------
* If you want to implement GDPR consent in your adapter, use the function
* ComplianceService.gdpr.getConsent() which will return an object.
*
* Here is what the values in that object mean:
* - applies: the boolean value indicating if the request is subject to
* GDPR regulations
* - consentString: the consent string developed by GDPR Consent Working
* Group under the auspices of IAB Europe
*
* The return object should look something like this:
* {
* applies: true,
* consentString: "BOQ7WlgOQ7WlgABABwAAABJOACgACAAQABA"
* }
*
* You can also determine whether or not the publisher has enabled privacy
* features in their wrapper by querying ComplianceService.isPrivacyEnabled().
*
* This function will return a boolean, which indicates whether the wrapper's
* privacy features are on (true) or off (false). If they are off, the values
* returned from gdpr.getConsent() are safe defaults and no attempt has been
* made by the wrapper to contact a Consent Management Platform.
*/
// var gdprStatus = ComplianceService.gdpr.getConsent();
// var privacyEnabled = ComplianceService.isPrivacyEnabled();
/* ---------------- Craft bid request using the above returnParcels --------- */
/* ------- Put GDPR consent code here if you are implementing GDPR ---------- */
/* -------------------------------------------------------------------------- */
return {
url: baseUrl,
data: queryObj,
callbackId: callbackId,
networkParamOverrides: {
method: 'POST',
contentType: 'text/plain'
// contentType: 'application/json'
}
};
}
/* =============================================================================
* STEP 3 | Response callback
* -----------------------------------------------------------------------------
*
* This generator is only necessary if the partner's endpoint has the ability
* to return an arbitrary ID that is sent to it. It should retrieve that ID from
* the response and save the response to adResponseStore keyed by that ID.
*
* If the endpoint does not have an appropriate field for this, set the profile's
* callback type to CallbackTypes.CALLBACK_NAME and omit this function.
*/
function adResponseCallback(adResponse) {
console.log("😻😻😻😻😻😻😻");
/* Get callbackId from adResponse here */
var callbackId = 0;
__baseClass._adResponseStore[callbackId] = adResponse;
}
/* -------------------------------------------------------------------------- */
/* Helpers
* ---------------------------------- */
/* =============================================================================
* STEP 5 | Rendering Pixel
* -----------------------------------------------------------------------------
*
*/
/**
* This function will render the pixel given.
* @param {string} pixelUrl Tracking pixel img url.
*/
function __renderPixel(pixelUrl) {
console.log('😻😻😻😻😻😻😻');
if (pixelUrl) {
Network.img({
url: decodeURIComponent(pixelUrl),
method: 'GET'
});
}
}
/**
* Parses and extracts demand from adResponse according to the adapter and then attaches it
* to the corresponding bid's returnParcel in the correct format using targeting keys.
*
* @param {string} sessionId The sessionId, used for stats and other events.
*
* @param {any} adResponse This is the bid response as returned from the bid request, that was either
* passed to a JSONP callback or simply sent back via AJAX.
*
* @param {object[]} returnParcels The array of original parcels, SAME array that was passed to
* generateRequestObj to signal which slots need demand. In this funciton, the demand needs to be
* attached to each one of the objects for which the demand was originally requested for.
*/
function __parseResponse(sessionId, adResponse, returnParcels) {
console.log('🤙🤙🤙🤙🤙🤙 PARSE RESPONSE');
console.log(sessionId);
console.log(adResponse);
console.log(returnParcels);
/* =============================================================================
* STEP 4 | Parse & store demand response
* -----------------------------------------------------------------------------
*
* Fill the below variables with information about the bid from the partner, using
* the adResponse variable that contains your module adResponse.
*/
/* This an array of all the bids in your response that will be iterated over below. Each of
* these will be mapped back to a returnParcel object using some criteria explained below.
* The following variables will also be parsed and attached to that returnParcel object as
* returned demand.
*
* Use the adResponse variable to extract your bid information and insert it into the
* bids array. Each element in the bids array should represent a single bid and should
* match up to a single element from the returnParcel array.
*
*/
/* ---------- Process adResponse and extract the bids into the bids array ------------ */
var bids = adResponse.bids;
/* --------------------------------------------------------------------------------- */
for (var j = 0; j < returnParcels.length; j++) {
var curReturnParcel = returnParcels[j];
var headerStatsInfo = {};
var htSlotId = curReturnParcel.htSlot.getId();
headerStatsInfo[htSlotId] = {};
headerStatsInfo[htSlotId][curReturnParcel.requestId] = [curReturnParcel.xSlotName];
var curBid;
for (var i = 0; i < bids.length; i++) {
/**
* This section maps internal returnParcels and demand returned from the bid request.
* In order to match them correctly, they must be matched via some criteria. This
* is usually some sort of placements or inventory codes. Please replace the someCriteria
* key to a key that represents the placement in the configuration and in the bid responses.
*/
/* ----------- Fill this out to find a matching bid for the current parcel ------------- */
if (curReturnParcel.xSlotName === bids[i].bidId) {
curBid = bids[i];
bids.splice(i, 1);
break;
}
}
/* No matching bid found so its a pass */
if (!curBid) {
if (__profile.enabledAnalytics.requestTime) {
__baseClass._emitStatsEvent(sessionId, 'hs_slot_pass', headerStatsInfo);
}
curReturnParcel.pass = true;
continue;
}
/* ---------- Fill the bid variables with data from the bid response here. ------------ */
/* Using the above variable, curBid, extract various information about the bid and assign it to
* these local variables */
/* The bid price for the given slot */
var bidPrice = Number(curBid.cpm);
/* The size of the given slot */
var bidSize = [Number(curBid.width), Number(curBid.height)];
/* The creative/adm for the given slot that will be rendered if is the winner.
* Please make sure the URL is decoded and ready to be document.written.
*/
var bidCreative = curBid.ad;
/* The dealId if applicable for this slot. */
var bidDealId = null; // curBid.creativeId;
/* Explicitly pass */
var bidIsPass = bidPrice <= 0;
/* OPTIONAL: tracking pixel url to be fired AFTER rendering a winning creative.
* If firing a tracking pixel is not required or the pixel url is part of the adm,
* leave empty;
*/
var pixelUrl = '';
/* --------------------------------------------------------------------------------------- */
curBid = null;
if (bidIsPass) {
//? if (DEBUG) {
Scribe.info(__profile.partnerId + ' returned pass for { id: ' + adResponse.id + ' }.');
//? }
if (__profile.enabledAnalytics.requestTime) {
__baseClass._emitStatsEvent(sessionId, 'hs_slot_pass', headerStatsInfo);
}
curReturnParcel.pass = true;
continue;
}
if (__profile.enabledAnalytics.requestTime) {
__baseClass._emitStatsEvent(sessionId, 'hs_slot_bid', headerStatsInfo);
}
curReturnParcel.size = bidSize;
curReturnParcel.targetingType = 'slot';
curReturnParcel.targeting = {};
var targetingCpm = '';
//? if (FEATURES.GPT_LINE_ITEMS) {
targetingCpm = __baseClass._bidTransformers.targeting.apply(bidPrice);
var sizeKey = Size.arrayToString(curReturnParcel.size);
if (bidDealId) {
curReturnParcel.targeting[__baseClass._configs.targetingKeys.pmid] = [sizeKey + '_' + bidDealId];
curReturnParcel.targeting[__baseClass._configs.targetingKeys.pm] = [sizeKey + '_' + targetingCpm];
} else {
curReturnParcel.targeting[__baseClass._configs.targetingKeys.om] = [sizeKey + '_' + targetingCpm];
}
curReturnParcel.targeting[__baseClass._configs.targetingKeys.id] = [curReturnParcel.requestId];
//? }
//? if (FEATURES.RETURN_CREATIVE) {
curReturnParcel.adm = bidCreative;
if (pixelUrl) {
curReturnParcel.winNotice = __renderPixel.bind(null, pixelUrl);
}
//? }
//? if (FEATURES.RETURN_PRICE) {
curReturnParcel.price = Number(__baseClass._bidTransformers.price.apply(bidPrice));
//? }
var expiry = 0;
if (__profile.features.demandExpiry.enabled) {
expiry = __profile.features.demandExpiry.value + System.now();
}
var pubKitAdId = RenderService.registerAd({
sessionId: sessionId,
partnerId: __profile.partnerId,
adm: bidCreative,
requestId: curReturnParcel.requestId,
size: curReturnParcel.size,
price: targetingCpm,
dealId: bidDealId || null,
timeOfExpiry: expiry,
auxFn: __renderPixel,
auxArgs: [pixelUrl]
});
console.log('🧼🧼🧼🧼');
console.log(pubKitAdId);
//? if (FEATURES.INTERNAL_RENDER) {
curReturnParcel.targeting.pubKitAdId = pubKitAdId;
//? }
}
}
/* =====================================
* Constructors
* ---------------------------------- */
(function __constructor() {
ComplianceService = SpaceCamp.services.ComplianceService;
RenderService = SpaceCamp.services.RenderService;
/* =============================================================================
* STEP 1 | Partner Configuration
* -----------------------------------------------------------------------------
*
* Please fill out the below partner profile according to the steps in the README doc.
*/
/* ---------- Please fill out this partner profile according to your module ------------ */
__profile = {
partnerId: 'VroomHtb',
namespace: 'VroomHtb',
statsId: 'VRO',
version: '2.0.0',
targetingType: 'slot',
enabledAnalytics: {
requestTime: true
},
features: {
demandExpiry: {
enabled: false,
value: 0
},
rateLimiting: {
enabled: false,
value: 0
}
},
/* Targeting keys for demand, should follow format ix_{statsId}_id */
targetingKeys: {
id: 'ix_vro_id',
om: 'ix_vro_cpm',
pm: 'ix_vro_cpm',
pmid: 'ix_vro_dealid'
},
/* The bid price unit (in cents) the endpoint returns, please refer to the readme for details */
bidUnitInCents: 1,
lineItemType: Constants.LineItemTypes.ID_AND_SIZE,
callbackType: Partner.CallbackTypes.NONE,
architecture: Partner.Architectures.SRA,
requestType: Partner.RequestTypes.ANY
};
/* --------------------------------------------------------------------------------------- */
//? if (DEBUG) {
var results = ConfigValidators.partnerBaseConfig(configs) || PartnerSpecificValidator(configs);
if (results) {
throw Whoopsie('INVALID_CONFIG', results);
}
//? }
__baseClass = Partner(__profile, configs, null, {
parseResponse: __parseResponse,
generateRequestObj: __generateRequestObj,
adResponseCallback: adResponseCallback
});
})();
/* =====================================
* Public Interface
* ---------------------------------- */
var derivedClass = {
/* Class Information
* ---------------------------------- */
//? if (DEBUG) {
__type__: 'VroomHtb',
//? }
//? if (TEST) {
__baseClass: __baseClass,
//? }
/* Data
* ---------------------------------- */
//? if (TEST) {
profile: __profile,
//? }
/* Functions
* ---------------------------------- */
//? if (TEST) {
parseResponse: __parseResponse,
generateRequestObj: __generateRequestObj,
adResponseCallback: adResponseCallback
//? }
};
return Classify.derive(__baseClass, derivedClass);
}
////////////////////////////////////////////////////////////////////////////////
// Exports /////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////
module.exports = VroomHtb;
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment