From bbfddb40827d55a516095621d51cf76cb21e7658 Mon Sep 17 00:00:00 2001 From: Filip Stamenkovic Date: Fri, 10 Dec 2021 14:10:52 +0100 Subject: [PATCH] pubwise bidder as an example --- .../gpt/native_ortb2_example.html | 143 ++++++++++ modules/pubwiseBidAdapter.js | 268 ++---------------- 2 files changed, 159 insertions(+), 252 deletions(-) create mode 100644 integrationExamples/gpt/native_ortb2_example.html diff --git a/integrationExamples/gpt/native_ortb2_example.html b/integrationExamples/gpt/native_ortb2_example.html new file mode 100644 index 000000000000..a6fe3bbf2477 --- /dev/null +++ b/integrationExamples/gpt/native_ortb2_example.html @@ -0,0 +1,143 @@ + + + + + + + + + + + + + + +

Prebid Native

+
+

No response

+ +
+ +
+
+ +
+

No response

+ +
+ + + + diff --git a/modules/pubwiseBidAdapter.js b/modules/pubwiseBidAdapter.js index a1b9ffb56a03..a7895c6ddd0e 100644 --- a/modules/pubwiseBidAdapter.js +++ b/modules/pubwiseBidAdapter.js @@ -1,4 +1,4 @@ -import { _each, isStr, deepClone, isArray, deepSetValue, inIframe, logMessage, logInfo, logWarn, logError } from '../src/utils.js'; +import { isStr, deepClone, isArray, deepSetValue, inIframe, logMessage, logInfo, logWarn, logError } from '../src/utils.js'; import { config } from '../src/config.js'; import { registerBidder } from '../src/adapters/bidderFactory.js'; import { BANNER, NATIVE } from '../src/mediaTypes.js'; @@ -10,8 +10,6 @@ const DEFAULT_CURRENCY = 'USD'; const AUCTION_TYPE = 1; const BIDDER_CODE = 'pwbid'; const ENDPOINT_URL = 'https://bid.pubwise.io/prebid'; -const DEFAULT_WIDTH = 0; -const DEFAULT_HEIGHT = 0; const PREBID_NATIVE_HELP_LINK = 'https://prebid.org/dev-docs/show-native-ads.html'; // const USERSYNC_URL = '//127.0.0.1:8080/usersync' @@ -22,68 +20,7 @@ const CUSTOM_PARAMS = { 'lon': '', // User Location - Longitude }; -// rtb native types are meant to be dynamic and extendable -// the extendable data asset types are nicely aligned -// in practice we set an ID that is distinct for each real type of return -const NATIVE_ASSETS = { - 'TITLE': { ID: 1, KEY: 'title', TYPE: 0 }, - 'IMAGE': { ID: 2, KEY: 'image', TYPE: 0 }, - 'ICON': { ID: 3, KEY: 'icon', TYPE: 0 }, - 'SPONSOREDBY': { ID: 4, KEY: 'sponsoredBy', TYPE: 1 }, - 'BODY': { ID: 5, KEY: 'body', TYPE: 2 }, - 'CLICKURL': { ID: 6, KEY: 'clickUrl', TYPE: 0 }, - 'VIDEO': { ID: 7, KEY: 'video', TYPE: 0 }, - 'EXT': { ID: 8, KEY: 'ext', TYPE: 0 }, - 'DATA': { ID: 9, KEY: 'data', TYPE: 0 }, - 'LOGO': { ID: 10, KEY: 'logo', TYPE: 0 }, - 'SPONSORED': { ID: 11, KEY: 'sponsored', TYPE: 1 }, - 'DESC': { ID: 12, KEY: 'data', TYPE: 2 }, - 'RATING': { ID: 13, KEY: 'rating', TYPE: 3 }, - 'LIKES': { ID: 14, KEY: 'likes', TYPE: 4 }, - 'DOWNLOADS': { ID: 15, KEY: 'downloads', TYPE: 5 }, - 'PRICE': { ID: 16, KEY: 'price', TYPE: 6 }, - 'SALEPRICE': { ID: 17, KEY: 'saleprice', TYPE: 7 }, - 'PHONE': { ID: 18, KEY: 'phone', TYPE: 8 }, - 'ADDRESS': { ID: 19, KEY: 'address', TYPE: 9 }, - 'DESC2': { ID: 20, KEY: 'desc2', TYPE: 10 }, - 'DISPLAYURL': { ID: 21, KEY: 'displayurl', TYPE: 11 }, - 'CTA': { ID: 22, KEY: 'cta', TYPE: 12 } -}; - -const NATIVE_ASSET_IMAGE_TYPE = { - 'ICON': 1, - 'LOGO': 2, - 'IMAGE': 3 -} - -// to render any native unit we have to have a few items -const NATIVE_MINIMUM_REQUIRED_IMAGE_ASSETS = [ - { - id: NATIVE_ASSETS.SPONSOREDBY.ID, - required: true, - data: { - type: 1 - } - }, - { - id: NATIVE_ASSETS.TITLE.ID, - required: true, - }, - { - id: NATIVE_ASSETS.IMAGE.ID, - required: true, - } -] - let isInvalidNativeRequest = false -let NATIVE_ASSET_ID_TO_KEY_MAP = {}; -let NATIVE_ASSET_KEY_TO_ASSET_MAP = {}; - -// together allows traversal of NATIVE_ASSETS_LIST in any direction -// id -> key -_each(NATIVE_ASSETS, anAsset => { NATIVE_ASSET_ID_TO_KEY_MAP[anAsset.ID] = anAsset.KEY }); -// key -> asset -_each(NATIVE_ASSETS, anAsset => { NATIVE_ASSET_KEY_TO_ASSET_MAP[anAsset.KEY] = anAsset }); export const spec = { code: BIDDER_CODE, @@ -292,7 +229,12 @@ export const spec = { function _checkMediaType(adm, newBid) { // Create a regex here to check the strings var admJSON = ''; - if (adm.indexOf('"ver":') >= 0) { + let startOfOpenRtb = adm.indexOf('"ver":'); + if (startOfOpenRtb >= 0) { + if (startOfOpenRtb > 1) { + let endOfJSON = adm.lastIndexOf('}'); + adm = adm.substring(startOfOpenRtb - 1, endOfJSON + 1); + } try { admJSON = JSON.parse(adm.replace(/\\/g, '')); if (admJSON && admJSON.assets) { @@ -309,60 +251,21 @@ function _checkMediaType(adm, newBid) { function _parseNativeResponse(bid, newBid) { newBid.native = {}; if (bid.hasOwnProperty('adm')) { - var adm = ''; + var adm = bid.adm; + let startOfOpenRtb = adm.indexOf('"ver":'); + if (startOfOpenRtb > 1) { + let endOfJSON = adm.lastIndexOf('}'); + adm = adm.substring(startOfOpenRtb - 1, endOfJSON + 1); + } try { - adm = JSON.parse(bid.adm.replace(/\\/g, '')); + adm = JSON.parse(adm.replace(/\\/g, '')); } catch (ex) { _logWarn('Error: Cannot parse native reponse for ad response: ' + newBid.adm); return; } if (adm && adm.assets && adm.assets.length > 0) { newBid.mediaType = NATIVE; - for (let i = 0, len = adm.assets.length; i < len; i++) { - switch (adm.assets[i].id) { - case NATIVE_ASSETS.TITLE.ID: - newBid.native.title = adm.assets[i].title && adm.assets[i].title.text; - break; - case NATIVE_ASSETS.IMAGE.ID: - newBid.native.image = { - url: adm.assets[i].img && adm.assets[i].img.url, - height: adm.assets[i].img && adm.assets[i].img.h, - width: adm.assets[i].img && adm.assets[i].img.w, - }; - break; - case NATIVE_ASSETS.ICON.ID: - newBid.native.icon = { - url: adm.assets[i].img && adm.assets[i].img.url, - height: adm.assets[i].img && adm.assets[i].img.h, - width: adm.assets[i].img && adm.assets[i].img.w, - }; - break; - case NATIVE_ASSETS.SPONSOREDBY.ID: - case NATIVE_ASSETS.BODY.ID: - case NATIVE_ASSETS.LIKES.ID: - case NATIVE_ASSETS.DOWNLOADS.ID: - case NATIVE_ASSETS.PRICE: - case NATIVE_ASSETS.SALEPRICE.ID: - case NATIVE_ASSETS.PHONE.ID: - case NATIVE_ASSETS.ADDRESS.ID: - case NATIVE_ASSETS.DESC2.ID: - case NATIVE_ASSETS.CTA.ID: - case NATIVE_ASSETS.RATING.ID: - case NATIVE_ASSETS.DISPLAYURL.ID: - newBid.native[NATIVE_ASSET_ID_TO_KEY_MAP[adm.assets[i].id]] = adm.assets[i].data && adm.assets[i].data.value; - break; - } - } - newBid.clickUrl = adm.link && adm.link.url; - newBid.clickTrackers = (adm.link && adm.link.clicktrackers) || []; - newBid.impressionTrackers = adm.imptrackers || []; - newBid.jstracker = adm.jstracker || []; - if (!newBid.width) { - newBid.width = DEFAULT_WIDTH; - } - if (!newBid.height) { - newBid.height = DEFAULT_HEIGHT; - } + newBid.native.ortb2 = adm; } } } @@ -533,19 +436,6 @@ function _initConf(refererInfo) { }; } -function _commonNativeRequestObject(nativeAsset, params) { - var key = nativeAsset.KEY; - return { - id: nativeAsset.ID, - required: params[key].required ? 1 : 0, - data: { - type: nativeAsset.TYPE, - len: params[key].len, - ext: params[key].ext - } - }; -} - function _addFloorFromFloorModule(impObj, bid) { let bidFloor = -1; // indicates no floor @@ -572,133 +462,7 @@ function _addFloorFromFloorModule(impObj, bid) { } function _createNativeRequest(params) { - var nativeRequestObject = { - assets: [] - }; - for (var key in params) { - if (params.hasOwnProperty(key)) { - var assetObj = {}; - if (!(nativeRequestObject.assets && nativeRequestObject.assets.length > 0 && nativeRequestObject.assets.hasOwnProperty(key))) { - switch (key) { - case NATIVE_ASSETS.TITLE.KEY: - if (params[key].len || params[key].length) { - assetObj = { - id: NATIVE_ASSETS.TITLE.ID, - required: params[key].required ? 1 : 0, - title: { - len: params[key].len || params[key].length, - ext: params[key].ext - } - }; - } else { - _logWarn('Error: Title Length is required for native ad: ' + JSON.stringify(params)); - } - break; - case NATIVE_ASSETS.IMAGE.KEY: - if (params[key].sizes && params[key].sizes.length > 0) { - assetObj = { - id: NATIVE_ASSETS.IMAGE.ID, - required: params[key].required ? 1 : 0, - img: { - type: NATIVE_ASSET_IMAGE_TYPE.IMAGE, - w: params[key].w || params[key].width || (params[key].sizes ? params[key].sizes[0] : UNDEFINED), - h: params[key].h || params[key].height || (params[key].sizes ? params[key].sizes[1] : UNDEFINED), - wmin: params[key].wmin || params[key].minimumWidth || (params[key].minsizes ? params[key].minsizes[0] : UNDEFINED), - hmin: params[key].hmin || params[key].minimumHeight || (params[key].minsizes ? params[key].minsizes[1] : UNDEFINED), - mimes: params[key].mimes, - ext: params[key].ext, - } - }; - } else { - _logWarn('Error: Image sizes is required for native ad: ' + JSON.stringify(params)); - } - break; - case NATIVE_ASSETS.ICON.KEY: - if (params[key].sizes && params[key].sizes.length > 0) { - assetObj = { - id: NATIVE_ASSETS.ICON.ID, - required: params[key].required ? 1 : 0, - img: { - type: NATIVE_ASSET_IMAGE_TYPE.ICON, - w: params[key].w || params[key].width || (params[key].sizes ? params[key].sizes[0] : UNDEFINED), - h: params[key].h || params[key].height || (params[key].sizes ? params[key].sizes[1] : UNDEFINED), - } - }; - } else { - _logWarn('Error: Icon sizes is required for native ad: ' + JSON.stringify(params)); - }; - break; - case NATIVE_ASSETS.VIDEO.KEY: - assetObj = { - id: NATIVE_ASSETS.VIDEO.ID, - required: params[key].required ? 1 : 0, - video: { - minduration: params[key].minduration, - maxduration: params[key].maxduration, - protocols: params[key].protocols, - mimes: params[key].mimes, - ext: params[key].ext - } - }; - break; - case NATIVE_ASSETS.EXT.KEY: - assetObj = { - id: NATIVE_ASSETS.EXT.ID, - required: params[key].required ? 1 : 0, - }; - break; - case NATIVE_ASSETS.LOGO.KEY: - assetObj = { - id: NATIVE_ASSETS.LOGO.ID, - required: params[key].required ? 1 : 0, - img: { - type: NATIVE_ASSET_IMAGE_TYPE.LOGO, - w: params[key].w || params[key].width || (params[key].sizes ? params[key].sizes[0] : UNDEFINED), - h: params[key].h || params[key].height || (params[key].sizes ? params[key].sizes[1] : UNDEFINED) - } - }; - break; - case NATIVE_ASSETS.SPONSOREDBY.KEY: - case NATIVE_ASSETS.BODY.KEY: - case NATIVE_ASSETS.RATING.KEY: - case NATIVE_ASSETS.LIKES.KEY: - case NATIVE_ASSETS.DOWNLOADS.KEY: - case NATIVE_ASSETS.PRICE.KEY: - case NATIVE_ASSETS.SALEPRICE.KEY: - case NATIVE_ASSETS.PHONE.KEY: - case NATIVE_ASSETS.ADDRESS.KEY: - case NATIVE_ASSETS.DESC2.KEY: - case NATIVE_ASSETS.DISPLAYURL.KEY: - case NATIVE_ASSETS.CTA.KEY: - assetObj = _commonNativeRequestObject(NATIVE_ASSET_KEY_TO_ASSET_MAP[key], params); - break; - } - } - } - if (assetObj && assetObj.id) { - nativeRequestObject.assets[nativeRequestObject.assets.length] = assetObj; - } - }; - - // for native image adtype prebid has to have few required assests i.e. title,sponsoredBy, image - // if any of these are missing from the request then request will not be sent - var requiredAssetCount = NATIVE_MINIMUM_REQUIRED_IMAGE_ASSETS.length; - var presentrequiredAssetCount = 0; - NATIVE_MINIMUM_REQUIRED_IMAGE_ASSETS.forEach(ele => { - var lengthOfExistingAssets = nativeRequestObject.assets.length; - for (var i = 0; i < lengthOfExistingAssets; i++) { - if (ele.id == nativeRequestObject.assets[i].id) { - presentrequiredAssetCount++; - break; - } - } - }); - if (requiredAssetCount == presentrequiredAssetCount) { - isInvalidNativeRequest = false; - } else { - isInvalidNativeRequest = true; - } - return nativeRequestObject; + return params.ortb2; } function _createBannerRequest(bid) {