diff --git a/modules/pixfutureBidAdapter.js b/modules/pixfutureBidAdapter.js new file mode 100644 index 00000000000..e5422f36358 --- /dev/null +++ b/modules/pixfutureBidAdapter.js @@ -0,0 +1,323 @@ +import { registerBidder } from '../src/adapters/bidderFactory.js'; +import { getStorageManager } from '../src/storageManager.js'; +import { BANNER } from '../src/mediaTypes.js'; +import { config } from '../src/config.js'; +import includes from 'core-js-pure/features/array/includes.js'; +import * as utils from '../src/utils.js'; +import { auctionManager } from '../src/auctionManager.js'; +import find from 'core-js-pure/features/array/find.js'; + +const SOURCE = 'pbjs'; +const storageManager = getStorageManager(); +const USER_PARAMS = ['age', 'externalUid', 'segments', 'gender', 'dnt', 'language']; +export const spec = { + code: 'pixfuture', + hostname: 'https://prebid-js.pixfuture.com', + + getHostname() { + let ret = this.hostname; + try { + ret = storageManager.getDataFromLocalStorage('ov_pixbidder_host') || ret; + } catch (e) { + } + return ret; + }, + + isBidRequestValid(bid) { + return !!(bid.sizes && bid.bidId && bid.params && + (bid.params.pix_id && (typeof bid.params.pix_id === 'string'))); + }, + + buildRequests(validBidRequests, bidderRequest) { + const tags = validBidRequests.map(bidToTag); + const hostname = this.getHostname(); + return validBidRequests.map((bidRequest) => { + let referer = ''; + if (bidderRequest && bidderRequest.refererInfo) { + referer = bidderRequest.refererInfo.referer || ''; + } + + const userObjBid = find(validBidRequests, hasUserInfo); + let userObj = {}; + if (config.getConfig('coppa') === true) { + userObj = {'coppa': true}; + } + + if (userObjBid) { + Object.keys(userObjBid.params.user) + .filter(param => includes(USER_PARAMS, param)) + .forEach((param) => { + let uparam = utils.convertCamelToUnderscore(param); + if (param === 'segments' && utils.isArray(userObjBid.params.user[param])) { + let segs = []; + userObjBid.params.user[param].forEach(val => { + if (utils.isNumber(val)) { + segs.push({'id': val}); + } else if (utils.isPlainObject(val)) { + segs.push(val); + } + }); + userObj[uparam] = segs; + } else if (param !== 'segments') { + userObj[uparam] = userObjBid.params.user[param]; + } + }); + } + + const schain = validBidRequests[0].schain; + + const payload = { + tags: [...tags], + user: userObj, + sdk: { + source: SOURCE, + version: '$prebid.version$' + }, + schain: schain + }; + + if (bidderRequest && bidderRequest.uspConsent) { + payload.us_privacy = bidderRequest.uspConsent + } + + if (bidderRequest && bidderRequest.refererInfo) { + let refererinfo = { + rd_ref: encodeURIComponent(bidderRequest.refererInfo.referer), + rd_top: bidderRequest.refererInfo.reachedTop, + rd_ifs: bidderRequest.refererInfo.numIframes, + rd_stk: bidderRequest.refererInfo.stack.map((url) => encodeURIComponent(url)).join(',') + }; + payload.referrer_detection = refererinfo; + } + + if (validBidRequests[0].userId) { + let eids = []; + + addUserId(eids, utils.deepAccess(validBidRequests[0], `userId.flocId.id`), 'chrome.com', null); + addUserId(eids, utils.deepAccess(validBidRequests[0], `userId.criteoId`), 'criteo.com', null); + addUserId(eids, utils.deepAccess(validBidRequests[0], `userId.unifiedId`), 'thetradedesk.com', null); + addUserId(eids, utils.deepAccess(validBidRequests[0], `userId.id5Id`), 'id5.io', null); + addUserId(eids, utils.deepAccess(validBidRequests[0], `userId.sharedId`), 'thetradedesk.com', null); + addUserId(eids, utils.deepAccess(validBidRequests[0], `userId.identityLink`), 'liveramp.com', null); + addUserId(eids, utils.deepAccess(validBidRequests[0], `userId.liveIntentId`), 'liveintent.com', null); + addUserId(eids, utils.deepAccess(validBidRequests[0], `userId.fabrickId`), 'home.neustar', null); + + if (eids.length) { + payload.eids = eids; + } + } + + if (tags[0].publisher_id) { + payload.publisher_id = tags[0].publisher_id; + } + + const ret = { + url: `${hostname}/`, + method: 'POST', + options: {withCredentials: false}, + data: { + v: $$PREBID_GLOBAL$$.version, + pageUrl: referer, + bidId: bidRequest.bidId, + auctionId: bidRequest.auctionId, + transactionId: bidRequest.transactionId, + adUnitCode: bidRequest.adUnitCode, + bidRequestCount: bidRequest.bidRequestCount, + sizes: bidRequest.sizes, + params: bidRequest.params, + pubext: payload + } + }; + if (bidderRequest && bidderRequest.gdprConsent) { + ret.data.gdprConsent = { + consentString: bidderRequest.gdprConsent.consentString, + consentRequired: (typeof bidderRequest.gdprConsent.gdprApplies === 'boolean') && bidderRequest.gdprConsent.gdprApplies + }; + } + return ret; + }); + }, + + interpretResponse: function (serverResponse, { bidderRequest }) { + serverResponse = serverResponse.body; + const bids = []; + if (serverResponse.creatives.bids && serverResponse.placements) { + serverResponse.placements.forEach(serverBid => { + serverBid.creatives.forEach(creative => { + const bid = newBid(serverBid, creative, serverBid.placement_id, serverBid.uuid); + bid.mediaType = BANNER; + bids.push(bid); + }); + }); + } + + return bids; + }, +}; + +function newBid(serverBid, rtbBid, placementId, uuid) { + const bid = { + requestId: uuid, + cpm: rtbBid.cpm, + creativeId: rtbBid.creative_id, + currency: 'USD', + netRevenue: true, + ttl: 300, + adUnitCode: placementId + }; + + if (rtbBid.adomain) { + bid.meta = Object.assign({}, bid.meta, { advertiserDomains: [rtbBid.adomain] }); + }; + + Object.assign(bid, { + width: rtbBid.width, + height: rtbBid.height, + ad: rtbBid.code + }); + + return bid; +} + +// Functions related optional parameters +function bidToTag(bid) { + const tag = {}; + tag.sizes = transformSizes(bid.sizes); + tag.primary_size = tag.sizes[0]; + tag.ad_types = []; + tag.uuid = bid.bidId; + if (bid.params.placementId) { + tag.id = parseInt(bid.params.placementId, 10); + } else { + tag.code = bid.params.invCode; + } + tag.allow_smaller_sizes = bid.params.allowSmallerSizes || false; + tag.use_pmt_rule = bid.params.usePaymentRule || false + tag.prebid = true; + tag.disable_psa = true; + let bidFloor = getBidFloor(bid); + if (bidFloor) { + tag.reserve = bidFloor; + } + if (bid.params.position) { + tag.position = {'above': 1, 'below': 2}[bid.params.position] || 0; + } + if (bid.params.trafficSourceCode) { + tag.traffic_source_code = bid.params.trafficSourceCode; + } + if (bid.params.privateSizes) { + tag.private_sizes = transformSizes(bid.params.privateSizes); + } + if (bid.params.supplyType) { + tag.supply_type = bid.params.supplyType; + } + if (bid.params.pubClick) { + tag.pubclick = bid.params.pubClick; + } + if (bid.params.extInvCode) { + tag.ext_inv_code = bid.params.extInvCode; + } + if (bid.params.publisherId) { + tag.publisher_id = parseInt(bid.params.publisherId, 10); + } + if (bid.params.externalImpId) { + tag.external_imp_id = bid.params.externalImpId; + } + if (!utils.isEmpty(bid.params.keywords)) { + let keywords = utils.transformBidderParamKeywords(bid.params.keywords); + + if (keywords.length > 0) { + keywords.forEach(deleteValues); + } + tag.keywords = keywords; + } + + let gpid = utils.deepAccess(bid, 'ortb2Imp.ext.data.pbadslot'); + if (gpid) { + tag.gpid = gpid; + } + + if (bid.renderer) { + tag.video = Object.assign({}, tag.video, {custom_renderer_present: true}); + } + + if (bid.params.frameworks && utils.isArray(bid.params.frameworks)) { + tag['banner_frameworks'] = bid.params.frameworks; + } + + let adUnit = find(auctionManager.getAdUnits(), au => bid.transactionId === au.transactionId); + if (adUnit && adUnit.mediaTypes && adUnit.mediaTypes.banner) { + tag.ad_types.push(BANNER); + } + + if (tag.ad_types.length === 0) { + delete tag.ad_types; + } + + return tag; +} + +function addUserId(eids, id, source, rti) { + if (id) { + if (rti) { + eids.push({source, id, rti_partner: rti}); + } else { + eids.push({source, id}); + } + } + return eids; +} + +function hasUserInfo(bid) { + return !!bid.params.user; +} + +function transformSizes(requestSizes) { + let sizes = []; + let sizeObj = {}; + + if (utils.isArray(requestSizes) && requestSizes.length === 2 && + !utils.isArray(requestSizes[0])) { + sizeObj.width = parseInt(requestSizes[0], 10); + sizeObj.height = parseInt(requestSizes[1], 10); + sizes.push(sizeObj); + } else if (typeof requestSizes === 'object') { + for (let i = 0; i < requestSizes.length; i++) { + let size = requestSizes[i]; + sizeObj = {}; + sizeObj.width = parseInt(size[0], 10); + sizeObj.height = parseInt(size[1], 10); + sizes.push(sizeObj); + } + } + + return sizes; +} + +function getBidFloor(bid) { + if (!utils.isFn(bid.getFloor)) { + return (bid.params.reserve) ? bid.params.reserve : null; + } + + let floor = bid.getFloor({ + currency: 'USD', + mediaType: '*', + size: '*' + }); + if (utils.isPlainObject(floor) && !isNaN(floor.floor) && floor.currency === 'USD') { + return floor.floor; + } + return null; +} + +function deleteValues(keyPairObj) { + if (isPopulatedArray(keyPairObj.value) && keyPairObj.value[0] === '') { + delete keyPairObj.value; + } +} + +function isPopulatedArray(arr) { + return !!(utils.isArray(arr) && arr.length > 0); +} + +registerBidder(spec); diff --git a/modules/pixfutureBidAdapter.md b/modules/pixfutureBidAdapter.md new file mode 100644 index 00000000000..950c08e72e2 --- /dev/null +++ b/modules/pixfutureBidAdapter.md @@ -0,0 +1,154 @@ +# Overview + +``` +Module Name: PixFuture Bid Adapter +Module Type: Bidder Adapter +Maintainer: admin@pixfuture.net +``` +# Description + +Module that connects to PixFuture demand sources + +# Test Parameters +``` +var adUnits = [{ + "bidderCode": "pixfuture", + "auctionId": "634c9d0e-306f-4a5c-974e-21697dfd4fcd", + "bidderRequestId": "5f85993da0f6be", + "bids": [ + { + "labelAny": [ + "display" + ], + "bidder": "pixfuture", + "params": { + "pix_id": "Abc123" + }]; +``` +# Test Example +... + + + + + + + + + + + +

Basic Prebid.js Example

+
Div-1
+
+ +
+ +
+ +
Div-2
+
+ +
+ + + + +... diff --git a/test/spec/modules/pixfutureBidAdapter_spec.js b/test/spec/modules/pixfutureBidAdapter_spec.js new file mode 100644 index 00000000000..a236478c9b4 --- /dev/null +++ b/test/spec/modules/pixfutureBidAdapter_spec.js @@ -0,0 +1,255 @@ +import { expect } from 'chai'; // may prefer 'assert' in place of 'expect' +import { spec } from 'modules/pixfutureBidAdapter.js'; +import { newBidder } from 'src/adapters/bidderFactory.js'; +import * as bidderFactory from 'src/adapters/bidderFactory.js'; +import { auctionManager } from 'src/auctionManager.js'; +import { deepClone } from 'src/utils.js'; +import { config } from 'src/config.js'; + +describe('PixFutureAdapter', function () { + it('', function () { + const adapter = newBidder(spec); + describe('inherited functions', function () { + it('exists and is a function', function () { + expect(adapter.callBids).to.exist.and.to.be.a('function'); + }); + }); + + // Test of isBidRequestValid method + + describe('isBidRequestValid', function () { + let bid = { + 'bidder': 'pixfuture', + 'pageUrl': 'https://adinify.com/prebidjs/?pbjs_debug=true', + 'bidId': '236e806f760f0c', + 'auctionId': 'aa7f5d76-806b-4e0d-b795-cd6bd84ddc63', + 'transactionId': '0fdf67c0-7b48-4fef-9716-cc64d948e95d', + 'adUnitCode': '26335x300x250x14x_ADSLOT88', + 'sizes': [[300, 250], [300, 600]], + 'params': { + 'pix_id': '777' + } + }; + it('should return true when required params found (bid)', function () { + expect(spec.isBidRequestValid(bid)).to.equal(true); + }); + + it('should return true when required params found (bid.param=true)', function () { + delete bid.params; + bid.params = { + 'pix_id': '777' + }; + expect(spec.isBidRequestValid(bid)).to.equal(true); + }); + + it('should return false when required params are not passed', function () { + let bid = Object.assign({}, bid); + delete bid.params; + bid.params = { + 'pix_id': 0 + }; + expect(spec.isBidRequestValid(bid)).to.equal(false); + }); + }); + + // Test of buildRequest method + + describe('Test of buildRequest method', function () { + let validBidRequests = [{ + 'labelAny': ['display'], + 'bidder': 'pixfuture', + 'params': { + 'pix_id': '777' + }, + 'userId': { + 'criteoId': 'P9iqSF9MSDVlZ2ZwdTVGanp5U2l0MWt1cnZya25TdEs4VlY4ZjNTeHQ4czlJUkZES0NFRXBZblJNcTNYYjU4MWxYS2VWalM5dnd5RUhRYm0lMkJuQUFNVm1iclRvSVJTYzBuNkcxZUtTa2duRyUyQnU4S3clM0Q', + 'id5id': { + 'uid': 'ID5-ZHMOcvSShIBZiIth_yYh9odjNFxVEmMQ_i5TArPfWw!ID5*dtrjfV5mPLasyya5TW2IE9oVzQZwx7xRPGyAYS4hcWkAAOoxoFef4bIoREpQys8x', + 'ext': { + 'linkType': 2 + } + }, + 'pubcid': 'e09ab6a3-ae74-4f01-b2e8-81b141d6dc61', + 'sharedid': { + 'id': '01EXPPGZ9C8NKG1MTXVHV98505', + 'third': '01EXPPGZ9C8NKG1MTXVHV98505' + } + }, + 'userIdAsEids': [{ + 'source': 'criteo.com', + 'uids': [{ + 'id': 'P9iqSF9MSDVlZ2ZwdTVGanp5U2l0MWt1cnZya25TdEs4VlY4ZjNTeHQ4czlJUkZES0NFRXBZblJNcTNYYjU4MWxYS2VWalM5dnd5RUhRYm0lMkJuQUFNVm1iclRvSVJTYzBuNkcxZUtTa2duRyUyQnU4S3clM0Q', + 'atype': 1 + }] + }, { + 'source': 'id5-sync.com', + 'uids': [{ + 'id': 'ID5-ZHMOcvSShIBZiIth_yYh9odjNFxVEmMQ_i5TArPfWw!ID5*dtrjfV5mPLasyya5TW2IE9oVzQZwx7xRPGyAYS4hcWkAAOoxoFef4bIoREpQys8x', + 'atype': 1, + 'ext': { + 'linkType': 2 + } + }] + }, { + 'source': 'pubcid.org', + 'uids': [{ + 'id': 'e09ab6a3-ae74-4f01-b2e8-81b141d6dc61', + 'atype': 1 + }] + }, { + 'source': 'sharedid.org', + 'uids': [{ + 'id': '01EXPPGZ9C8NKG1MTXVHV98505', + 'atype': 1, + 'ext': { + 'third': '01EXPPGZ9C8NKG1MTXVHV98505' + } + }] + }], + 'crumbs': { + 'pubcid': 'e09ab6a3-ae74-4f01-b2e8-81b141d6dc61' + }, + 'mediaTypes': { + 'banner': { + 'sizes': [ + [300, 250] + ] + } + }, + 'adUnitCode': '26335x300x250x14x_ADSLOT88', + 'transactionId': '09310832-cd12-478c-86dd-fbd819dff9d3', + 'sizes': [ + [300, 250] + ], + 'bidId': '279272f27dfb3e', + 'bidderRequestId': '10a0de227377a3', + 'auctionId': '4cd5684b-ae2a-4d1f-84be-5f1ee66d9ff3', + 'src': 'client', + 'bidRequestsCount': 1, + 'bidderRequestsCount': 1, + 'bidderWinsCount': 0, + 'schain': { + 'ver': '1.0', + 'complete': 1, + 'nodes': [{ + 'asi': 'pixfuture.com', + 'sid': '14', + 'hp': 1 + }] + } + }]; + + let bidderRequests = + { + 'bidderCode': 'pixfuture', + 'auctionId': '4cd5684b-ae2a-4d1f-84be-5f1ee66d9ff3', + 'bidderRequestId': '10a0de227377a3', + 'bids': [{ + 'labelAny': ['display'], + 'bidder': 'pixfuture', + 'params': { + 'pix_id': '777' + }, + 'userId': { + 'criteoId': 'P9iqSF9MSDVlZ2ZwdTVGanp5U2l0MWt1cnZya25TdEs4VlY4ZjNTeHQ4czlJUkZES0NFRXBZblJNcTNYYjU4MWxYS2VWalM5dnd5RUhRYm0lMkJuQUFNVm1iclRvSVJTYzBuNkcxZUtTa2duRyUyQnU4S3clM0Q', + 'id5id': { + 'uid': 'ID5-ZHMOcvSShIBZiIth_yYh9odjNFxVEmMQ_i5TArPfWw!ID5*dtrjfV5mPLasyya5TW2IE9oVzQZwx7xRPGyAYS4hcWkAAOoxoFef4bIoREpQys8x', + 'ext': { + 'linkType': 2 + } + }, + 'pubcid': 'e09ab6a3-ae74-4f01-b2e8-81b141d6dc61', + 'sharedid': { + 'id': '01EXPPGZ9C8NKG1MTXVHV98505', + 'third': '01EXPPGZ9C8NKG1MTXVHV98505' + } + }, + 'userIdAsEids': [{ + 'source': 'criteo.com', + 'uids': [{ + 'id': 'P9iqSF9MSDVlZ2ZwdTVGanp5U2l0MWt1cnZya25TdEs4VlY4ZjNTeHQ4czlJUkZES0NFRXBZblJNcTNYYjU4MWxYS2VWalM5dnd5RUhRYm0lMkJuQUFNVm1iclRvSVJTYzBuNkcxZUtTa2duRyUyQnU4S3clM0Q', + 'atype': 1 + }] + }, { + 'source': 'id5-sync.com', + 'uids': [{ + 'id': 'ID5-ZHMOcvSShIBZiIth_yYh9odjNFxVEmMQ_i5TArPfWw!ID5*dtrjfV5mPLasyya5TW2IE9oVzQZwx7xRPGyAYS4hcWkAAOoxoFef4bIoREpQys8x', + 'atype': 1, + 'ext': { + 'linkType': 2 + } + }] + }, { + 'source': 'pubcid.org', + 'uids': [{ + 'id': 'e09ab6a3-ae74-4f01-b2e8-81b141d6dc61', + 'atype': 1 + }] + }, { + 'source': 'sharedid.org', + 'uids': [{ + 'id': '01EXPPGZ9C8NKG1MTXVHV98505', + 'atype': 1, + 'ext': { + 'third': '01EXPPGZ9C8NKG1MTXVHV98505' + } + }] + }], + 'crumbs': { + 'pubcid': 'e09ab6a3-ae74-4f01-b2e8-81b141d6dc61' + }, + 'mediaTypes': { + 'banner': { + 'sizes': [ + [300, 250] + ] + } + }, + 'adUnitCode': '26335x300x250x14x_ADSLOT88', + 'transactionId': '09310832-cd12-478c-86dd-fbd819dff9d3', + 'sizes': [ + [300, 250] + ], + 'bidId': '279272f27dfb3e', + 'bidderRequestId': '10a0de227377a3', + 'auctionId': '4cd5684b-ae2a-4d1f-84be-5f1ee66d9ff3', + 'src': 'client', + 'bidRequestsCount': 1, + 'bidderRequestsCount': 1, + 'bidderWinsCount': 0, + 'schain': { + 'ver': '1.0', + 'complete': 1, + 'nodes': [{ + 'asi': 'pixfuture.com', + 'sid': '14', + 'hp': 1 + }] + } + }], + 'auctionStart': 1620934247115, + 'timeout': 3000, + 'refererInfo': { + 'referer': 'https://adinify.com/prebidjs/?pbjs_debug=true', + 'reachedTop': true, + 'isAmp': false, + 'numIframes': 0, + 'stack': ['https://adinify.com/prebidjs/?pbjs_debug=true'], + 'canonicalUrl': null + }, + 'start': 1620934247117 + }; + + // let bidderRequest = Object.assign({}, bidderRequests); + const request = spec.buildRequests(validBidRequests, bidderRequests); + // console.log(JSON.stringify(request)); + let bidRequest = Object.assign({}, request[0]); + + expect(bidRequest.data).to.exist; + expect(bidRequest.data.sizes).to.deep.equal([[300, 250]]); + expect(bidRequest.data.params).to.deep.equal({'pix_id': '777'}); + expect(bidRequest.data.adUnitCode).to.deep.equal('26335x300x250x14x_ADSLOT88'); + }); + }); + // Add other `describe` or `it` blocks as necessary +});