From 5af57443403fe89d82be4d22a7573d1e64ba5709 Mon Sep 17 00:00:00 2001 From: adxpremium Date: Tue, 19 Oct 2021 17:48:27 +0200 Subject: [PATCH 01/17] LuponMedia BidAdapter 5.X --- modules/luponmediaBidAdapter.js | 569 ++++++++++++++++++ .../spec/modules/luponmediaBidAdapter_spec.js | 412 +++++++++++++ 2 files changed, 981 insertions(+) create mode 100755 modules/luponmediaBidAdapter.js create mode 100755 test/spec/modules/luponmediaBidAdapter_spec.js diff --git a/modules/luponmediaBidAdapter.js b/modules/luponmediaBidAdapter.js new file mode 100755 index 00000000000..84ff0add012 --- /dev/null +++ b/modules/luponmediaBidAdapter.js @@ -0,0 +1,569 @@ +import {isArray, logMessage, deepAccess, logWarn, parseSizesInput, deepSetValue, generateUUID, isEmpty, logError, _each} from '../src/utils.js'; +import {registerBidder} from '../src/adapters/bidderFactory.js'; +import {config} from '../src/config.js'; +import {BANNER} from '../src/mediaTypes.js'; +import { ajax } from '../src/ajax.js'; + +const BIDDER_CODE = 'luponmedia'; +const ENDPOINT_URL = 'https://rtb.adxpremium.services/openrtb2/auction'; + +const DIGITRUST_PROP_NAMES = { + PREBID_SERVER: { + id: 'id', + keyv: 'keyv' + } +}; + +var sizeMap = { + 1: '468x60', + 2: '728x90', + 5: '120x90', + 7: '125x125', + 8: '120x600', + 9: '160x600', + 10: '300x600', + 13: '200x200', + 14: '250x250', + 15: '300x250', + 16: '336x280', + 17: '240x400', + 19: '300x100', + 31: '980x120', + 32: '250x360', + 33: '180x500', + 35: '980x150', + 37: '468x400', + 38: '930x180', + 39: '750x100', + 40: '750x200', + 41: '750x300', + 42: '2x4', + 43: '320x50', + 44: '300x50', + 48: '300x300', + 53: '1024x768', + 54: '300x1050', + 55: '970x90', + 57: '970x250', + 58: '1000x90', + 59: '320x80', + 60: '320x150', + 61: '1000x1000', + 64: '580x500', + 65: '640x480', + 66: '930x600', + 67: '320x480', + 68: '1800x1000', + 72: '320x320', + 73: '320x160', + 78: '980x240', + 79: '980x300', + 80: '980x400', + 83: '480x300', + 85: '300x120', + 90: '548x150', + 94: '970x310', + 95: '970x100', + 96: '970x210', + 101: '480x320', + 102: '768x1024', + 103: '480x280', + 105: '250x800', + 108: '320x240', + 113: '1000x300', + 117: '320x100', + 125: '800x250', + 126: '200x600', + 144: '980x600', + 145: '980x150', + 152: '1000x250', + 156: '640x320', + 159: '320x250', + 179: '250x600', + 195: '600x300', + 198: '640x360', + 199: '640x200', + 213: '1030x590', + 214: '980x360', + 221: '1x1', + 229: '320x180', + 230: '2000x1400', + 232: '580x400', + 234: '6x6', + 251: '2x2', + 256: '480x820', + 257: '400x600', + 258: '500x200', + 259: '998x200', + 264: '970x1000', + 265: '1920x1080', + 274: '1800x200', + 278: '320x500', + 282: '320x400', + 288: '640x380', + 548: '500x1000', + 550: '980x480', + 552: '300x200', + 558: '640x640' +}; +_each(sizeMap, (item, key) => sizeMap[item] = key); + +export const spec = { + code: BIDDER_CODE, + supportedMediaTypes: [BANNER], + isBidRequestValid: function (bid) { + return !!(bid.params && bid.params.siteId && bid.params.keyId); // TODO: check for siteId and keyId + }, + buildRequests: function (bidRequests, bidderRequest) { + const bRequest = { + method: 'POST', + url: ENDPOINT_URL, + data: null, + options: {}, + bidderRequest + }; + + let currentImps = []; + + for (let i = 0, len = bidRequests.length; i < len; i++) { + let newReq = newOrtbBidRequest(bidRequests[i], bidderRequest, currentImps); + currentImps = newReq.imp; + bRequest.data = JSON.stringify(newReq); + } + + return bRequest; + }, + interpretResponse: (response, request) => { + const bidResponses = []; + var respCur = 'USD'; + let parsedRequest = JSON.parse(request.data); + let parsedReferrer = parsedRequest.site && parsedRequest.site.ref ? parsedRequest.site.ref : ''; + try { + if (response.body && response.body.seatbid && isArray(response.body.seatbid)) { + // Supporting multiple bid responses for same adSize + respCur = response.body.cur || respCur; + response.body.seatbid.forEach(seatbidder => { + seatbidder.bid && + isArray(seatbidder.bid) && + seatbidder.bid.forEach(bid => { + let newBid = { + requestId: bid.impid, + cpm: (parseFloat(bid.price) || 0).toFixed(2), + width: bid.w, + height: bid.h, + creativeId: bid.crid || bid.id, + dealId: bid.dealid, + currency: respCur, + netRevenue: false, + ttl: 300, + referrer: parsedReferrer, + ad: bid.adm + }; + + bidResponses.push(newBid); + }); + }); + } + } catch (error) { + logError(error); + } + return bidResponses; + }, + getUserSyncs: function (syncOptions, responses, gdprConsent, uspConsent) { + let allUserSyncs = []; + if (!hasSynced && (syncOptions.iframeEnabled || syncOptions.pixelEnabled)) { + responses.forEach(csResp => { + if (csResp.body && csResp.body.ext && csResp.body.ext.usersyncs) { + try { + let response = csResp.body.ext.usersyncs + let bidders = response.bidder_status; + for (let synci in bidders) { + let thisSync = bidders[synci]; + if (thisSync.no_cookie) { + let url = thisSync.usersync.url; + let type = thisSync.usersync.type; + + if (!url) { + logError(`No sync url for bidder luponmedia.`); + } else if ((type === 'image' || type === 'redirect') && syncOptions.pixelEnabled) { + logMessage(`Invoking image pixel user sync for luponmedia`); + allUserSyncs.push({type: 'image', url: url}); + } else if (type == 'iframe' && syncOptions.iframeEnabled) { + logMessage(`Invoking iframe user sync for luponmedia`); + allUserSyncs.push({type: 'iframe', url: url}); + } else { + logError(`User sync type "${type}" not supported for luponmedia`); + } + } + } + } catch (e) { + logError(e); + } + } + }); + } else { + logWarn('Luponmedia: Please enable iframe/pixel based user sync.'); + } + + hasSynced = true; + return allUserSyncs; + }, + onBidWon: bid => { + const bidString = JSON.stringify(bid); + spec.sendWinningsToServer(bidString); + }, + sendWinningsToServer: data => { + let mutation = `mutation {createWin(input: {win: {eventData: "${window.btoa(data)}"}}) {win {createTime } } }`; + let dataToSend = JSON.stringify({ query: mutation }); + + ajax('https://analytics.adxpremium.services/graphql', null, dataToSend, { + contentType: 'application/json', + method: 'POST' + }); + } +}; + +export function hasValidSupplyChainParams(schain) { + let isValid = false; + const requiredFields = ['asi', 'sid', 'hp']; + if (!schain.nodes) return isValid; + isValid = schain.nodes.reduce((status, node) => { + if (!status) return status; + return requiredFields.every(field => node[field]); + }, true); + if (!isValid) logError('LuponMedia: required schain params missing'); + return isValid; +} + +var hasSynced = false; + +export function resetUserSync() { + hasSynced = false; +} + +export function masSizeOrdering(sizes) { + const MAS_SIZE_PRIORITY = [15, 2, 9]; + + return sizes.sort((first, second) => { + // sort by MAS_SIZE_PRIORITY priority order + const firstPriority = MAS_SIZE_PRIORITY.indexOf(first); + const secondPriority = MAS_SIZE_PRIORITY.indexOf(second); + + if (firstPriority > -1 || secondPriority > -1) { + if (firstPriority === -1) { + return 1; + } + if (secondPriority === -1) { + return -1; + } + return firstPriority - secondPriority; + } + + // and finally ascending order + return first - second; + }); +} + +function newOrtbBidRequest(bidRequest, bidderRequest, currentImps) { + bidRequest.startTime = new Date().getTime(); + + const bannerParams = deepAccess(bidRequest, 'mediaTypes.banner'); + + let bannerSizes = []; + + if (bannerParams && bannerParams.sizes) { + const sizes = parseSizesInput(bannerParams.sizes); + + // get banner sizes in form [{ w: , h: }, ...] + const format = sizes.map(size => { + const [ width, height ] = size.split('x'); + const w = parseInt(width, 10); + const h = parseInt(height, 10); + return { w, h }; + }); + + bannerSizes = format; + } + + const data = { + id: bidRequest.transactionId, + test: config.getConfig('debug') ? 1 : 0, + source: { + tid: bidRequest.transactionId + }, + tmax: config.getConfig('timeout') || 1500, + imp: currentImps.concat([{ + id: bidRequest.bidId, + secure: 1, + ext: { + [bidRequest.bidder]: bidRequest.params + }, + banner: { + format: bannerSizes + } + }]), + ext: { + prebid: { + targeting: { + includewinners: true, + // includebidderkeys always false for openrtb + includebidderkeys: false + } + } + }, + user: { + } + } + + let bidFloor; + if (typeof bidRequest.getFloor === 'function' && !config.getConfig('disableFloors')) { + let floorInfo; + try { + floorInfo = bidRequest.getFloor({ + currency: 'USD', + mediaType: 'video', + size: parseSizes(bidRequest, 'video') + }); + } catch (e) { + logError('LuponMedia: getFloor threw an error: ', e); + } + bidFloor = typeof floorInfo === 'object' && floorInfo.currency === 'USD' && !isNaN(parseInt(floorInfo.floor)) ? parseFloat(floorInfo.floor) : undefined; + } else { + bidFloor = parseFloat(deepAccess(bidRequest, 'params.floor')); + } + if (!isNaN(bidFloor)) { + data.imp[0].bidfloor = bidFloor; + } + + appendSiteAppDevice(data, bidRequest, bidderRequest); + + const digiTrust = _getDigiTrustQueryParams(bidRequest, 'PREBID_SERVER'); + if (digiTrust) { + deepSetValue(data, 'user.ext.digitrust', digiTrust); + } + + if (bidderRequest.gdprConsent) { + // note - gdprApplies & consentString may be undefined in certain use-cases for consentManagement module + let gdprApplies; + if (typeof bidderRequest.gdprConsent.gdprApplies === 'boolean') { + gdprApplies = bidderRequest.gdprConsent.gdprApplies ? 1 : 0; + } + + deepSetValue(data, 'regs.ext.gdpr', gdprApplies); + deepSetValue(data, 'user.ext.consent', bidderRequest.gdprConsent.consentString); + } + + if (bidderRequest.uspConsent) { + deepSetValue(data, 'regs.ext.us_privacy', bidderRequest.uspConsent); + } + + // Set user uuid + deepSetValue(data, 'user.id', generateUUID()); + + // set crumbs + if (bidRequest.crumbs && bidRequest.crumbs.pubcid) { + deepSetValue(data, 'user.buyeruid', bidRequest.crumbs.pubcid); + } else { + deepSetValue(data, 'user.buyeruid', generateUUID()); + } + + if (bidRequest.userId && typeof bidRequest.userId === 'object' && + (bidRequest.userId.tdid || bidRequest.userId.pubcid || bidRequest.userId.lipb || bidRequest.userId.idl_env)) { + deepSetValue(data, 'user.ext.eids', []); + + if (bidRequest.userId.tdid) { + data.user.ext.eids.push({ + source: 'adserver.org', + uids: [{ + id: bidRequest.userId.tdid, + ext: { + rtiPartner: 'TDID' + } + }] + }); + } + + if (bidRequest.userId.pubcid) { + data.user.ext.eids.push({ + source: 'pubcommon', + uids: [{ + id: bidRequest.userId.pubcid, + }] + }); + } + + // support liveintent ID + if (bidRequest.userId.lipb && bidRequest.userId.lipb.lipbid) { + data.user.ext.eids.push({ + source: 'liveintent.com', + uids: [{ + id: bidRequest.userId.lipb.lipbid + }] + }); + + data.user.ext.tpid = { + source: 'liveintent.com', + uid: bidRequest.userId.lipb.lipbid + }; + + if (Array.isArray(bidRequest.userId.lipb.segments) && bidRequest.userId.lipb.segments.length) { + deepSetValue(data, 'rp.target.LIseg', bidRequest.userId.lipb.segments); + } + } + + // support identityLink (aka LiveRamp) + if (bidRequest.userId.idl_env) { + data.user.ext.eids.push({ + source: 'liveramp.com', + uids: [{ + id: bidRequest.userId.idl_env + }] + }); + } + } + + if (config.getConfig('coppa') === true) { + deepSetValue(data, 'regs.coppa', 1); + } + + if (bidRequest.schain && hasValidSupplyChainParams(bidRequest.schain)) { + deepSetValue(data, 'source.ext.schain', bidRequest.schain); + } + + const siteData = Object.assign({}, bidRequest.params.inventory, config.getConfig('fpd.context')); + const userData = Object.assign({}, bidRequest.params.visitor, config.getConfig('fpd.user')); + + if (!isEmpty(siteData) || !isEmpty(userData)) { + const bidderData = { + bidders: [ bidderRequest.bidderCode ], + config: { + fpd: {} + } + }; + + if (!isEmpty(siteData)) { + bidderData.config.fpd.site = siteData; + } + + if (!isEmpty(userData)) { + bidderData.config.fpd.user = userData; + } + + deepSetValue(data, 'ext.prebid.bidderconfig.0', bidderData); + } + + const pbAdSlot = deepAccess(bidRequest, 'fpd.context.pbAdSlot'); + if (typeof pbAdSlot === 'string' && pbAdSlot) { + deepSetValue(data.imp[0].ext, 'context.data.adslot', pbAdSlot); + } + + return data; +} + +function _getDigiTrustQueryParams(bidRequest = {}, endpointName) { + if (!endpointName || !DIGITRUST_PROP_NAMES[endpointName]) { + return null; + } + const propNames = DIGITRUST_PROP_NAMES[endpointName]; + + function getDigiTrustId() { + const bidRequestDigitrust = deepAccess(bidRequest, 'userId.digitrustid.data'); + if (bidRequestDigitrust) { + return bidRequestDigitrust; + } + + let digiTrustUser = (window.DigiTrust && (config.getConfig('digiTrustId') || window.DigiTrust.getUser({member: 'T9QSFKPDN9'}))); + return (digiTrustUser && digiTrustUser.success && digiTrustUser.identity) || null; + } + + let digiTrustId = getDigiTrustId(); + // Verify there is an ID and this user has not opted out + if (!digiTrustId || (digiTrustId.privacy && digiTrustId.privacy.optout)) { + return null; + } + + const digiTrustQueryParams = { + [propNames.id]: digiTrustId.id, + [propNames.keyv]: digiTrustId.keyv + }; + if (propNames.pref) { + digiTrustQueryParams[propNames.pref] = 0; + } + return digiTrustQueryParams; +} + +function _getPageUrl(bidRequest, bidderRequest) { + let pageUrl = config.getConfig('pageUrl'); + if (bidRequest.params.referrer) { + pageUrl = bidRequest.params.referrer; + } else if (!pageUrl) { + pageUrl = bidderRequest.refererInfo.referer; + } + return bidRequest.params.secure ? pageUrl.replace(/^http:/i, 'https:') : pageUrl; +} + +function appendSiteAppDevice(data, bidRequest, bidderRequest) { + if (!data) return; + + // ORTB specifies app OR site + if (typeof config.getConfig('app') === 'object') { + data.app = config.getConfig('app'); + } else { + data.site = { + page: _getPageUrl(bidRequest, bidderRequest) + } + } + if (typeof config.getConfig('device') === 'object') { + data.device = config.getConfig('device'); + } +} + +/** + * @param sizes + * @returns {*} + */ +function mapSizes(sizes) { + return parseSizesInput(sizes) + // map sizes while excluding non-matches + .reduce((result, size) => { + let mappedSize = parseInt(sizeMap[size], 10); + if (mappedSize) { + result.push(mappedSize); + } + return result; + }, []); +} + +function parseSizes(bid, mediaType) { + let params = bid.params; + if (mediaType === 'video') { + let size = []; + if (params.video && params.video.playerWidth && params.video.playerHeight) { + size = [ + params.video.playerWidth, + params.video.playerHeight + ]; + } else if (Array.isArray(deepAccess(bid, 'mediaTypes.video.playerSize')) && bid.mediaTypes.video.playerSize.length === 1) { + size = bid.mediaTypes.video.playerSize[0]; + } else if (Array.isArray(bid.sizes) && bid.sizes.length > 0 && Array.isArray(bid.sizes[0]) && bid.sizes[0].length > 1) { + size = bid.sizes[0]; + } + return size; + } + + // deprecated: temp legacy support + let sizes = []; + if (Array.isArray(params.sizes)) { + sizes = params.sizes; + } else if (typeof deepAccess(bid, 'mediaTypes.banner.sizes') !== 'undefined') { + sizes = mapSizes(bid.mediaTypes.banner.sizes); + } else if (Array.isArray(bid.sizes) && bid.sizes.length > 0) { + sizes = mapSizes(bid.sizes) + } else { + logWarn('LuponMedia: no sizes are setup or found'); + } + + return masSizeOrdering(sizes); +} + +registerBidder(spec); diff --git a/test/spec/modules/luponmediaBidAdapter_spec.js b/test/spec/modules/luponmediaBidAdapter_spec.js new file mode 100755 index 00000000000..8aeecc87c98 --- /dev/null +++ b/test/spec/modules/luponmediaBidAdapter_spec.js @@ -0,0 +1,412 @@ +import { resetUserSync, spec, hasValidSupplyChainParams } from 'modules/luponmediaBidAdapter.js'; +const ENDPOINT_URL = 'https://rtb.adxpremium.services/openrtb2/auction'; + +describe('luponmediaBidAdapter', function () { + describe('isBidRequestValid', function () { + let bid = { + 'bidder': 'luponmedia', + 'params': { + 'siteId': 12345, + 'keyId': '4o2c4' + }, + 'adUnitCode': 'test-div', + 'sizes': [[300, 250]], + 'bidId': 'g1987234bjkads', + 'bidderRequestId': '290348ksdhkas89324', + 'auctionId': '20384rlek235', + }; + + it('should return true when required params are found', function () { + 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 = { + 'siteId': 12345 + }; + expect(spec.isBidRequestValid(bid)).to.equal(false); + }); + }); + + describe('buildRequests', function () { + let bidRequests = [ + { + 'bidder': 'luponmedia', + 'params': { + 'siteId': 303522, + 'keyId': '4o2c4' + }, + 'crumbs': { + 'pubcid': '8d8b16cb-1383-4a0f-b4bb-0be28464d974' + }, + 'mediaTypes': { + 'banner': { + 'sizes': [ + [ + 300, + 250 + ] + ] + } + }, + 'adUnitCode': 'div-gpt-ad-1533155193780-2', + 'transactionId': '585d96a5-bd93-4a89-b8ea-0f546f3aaa82', + 'sizes': [ + [ + 300, + 250 + ] + ], + 'bidId': '268a30af10dd6f', + 'bidderRequestId': '140411b5010a2a', + 'auctionId': '7376c117-b7aa-49f5-a661-488543deeefd', + 'src': 'client', + 'bidRequestsCount': 1, + 'bidderRequestsCount': 1, + 'bidderWinsCount': 0, + 'schain': { + 'ver': '1.0', + 'complete': 1, + 'nodes': [ + { + 'asi': 'novi.ba', + 'sid': '199424', + 'hp': 1 + } + ] + } + } + ]; + + let bidderRequest = { + 'bidderCode': 'luponmedia', + 'auctionId': '7376c117-b7aa-49f5-a661-488543deeefd', + 'bidderRequestId': '140411b5010a2a', + 'bids': [ + { + 'bidder': 'luponmedia', + 'params': { + 'siteId': 303522, + 'keyId': '4o2c4' + }, + 'crumbs': { + 'pubcid': '8d8b16cb-1383-4a0f-b4bb-0be28464d974' + }, + 'mediaTypes': { + 'banner': { + 'sizes': [ + [ + 300, + 250 + ] + ] + } + }, + 'adUnitCode': 'div-gpt-ad-1533155193780-2', + 'transactionId': '585d96a5-bd93-4a89-b8ea-0f546f3aaa82', + 'sizes': [ + [ + 300, + 250 + ] + ], + 'bidId': '268a30af10dd6f', + 'bidderRequestId': '140411b5010a2a', + 'auctionId': '7376c117-b7aa-49f5-a661-488543deeefd', + 'src': 'client', + 'bidRequestsCount': 1, + 'bidderRequestsCount': 1, + 'bidderWinsCount': 0, + 'schain': { + 'ver': '1.0', + 'complete': 1, + 'nodes': [ + { + 'asi': 'novi.ba', + 'sid': '199424', + 'hp': 1 + } + ] + } + } + ], + 'auctionStart': 1587413920820, + 'timeout': 2000, + 'refererInfo': { + 'referer': 'https://novi.ba/clanak/176067/fast-car-beginner-s-guide-to-tuning-turbo-engines', + 'reachedTop': true, + 'numIframes': 0, + 'stack': [ + 'https://novi.ba/clanak/176067/fast-car-beginner-s-guide-to-tuning-turbo-engines' + ] + }, + 'start': 1587413920835 + }; + + it('sends bid request to ENDPOINT via POST', function () { + const requests = spec.buildRequests(bidRequests, bidderRequest); + let dynRes = JSON.parse(requests.data); + expect(requests.url).to.equal(ENDPOINT_URL); + expect(requests.method).to.equal('POST'); + expect(requests.data).to.equal('{"id":"585d96a5-bd93-4a89-b8ea-0f546f3aaa82","test":0,"source":{"tid":"585d96a5-bd93-4a89-b8ea-0f546f3aaa82","ext":{"schain":{"ver":"1.0","complete":1,"nodes":[{"asi":"novi.ba","sid":"199424","hp":1}]}}},"tmax":1500,"imp":[{"id":"268a30af10dd6f","secure":1,"ext":{"luponmedia":{"siteId":303522,"keyId":"4o2c4"}},"banner":{"format":[{"w":300,"h":250}]}}],"ext":{"prebid":{"targeting":{"includewinners":true,"includebidderkeys":false}}},"user":{"id":"' + dynRes.user.id + '","buyeruid":"8d8b16cb-1383-4a0f-b4bb-0be28464d974"},"site":{"page":"https://novi.ba/clanak/176067/fast-car-beginner-s-guide-to-tuning-turbo-engines"}}'); + }); + }); + + describe('interpretResponse', function () { + it('should get correct banner bid response', function () { + let response = { + 'id': '4776d680-15a2-45c3-bad5-db6bebd94a06', + 'seatbid': [ + { + 'bid': [ + { + 'id': '2a122246ef72ea', + 'impid': '2a122246ef72ea', + 'price': 0.43, + 'adm': ' ', + 'adid': '56380110', + 'cid': '44724710', + 'crid': '443801010', + 'w': 300, + 'h': 250, + 'ext': { + 'prebid': { + 'targeting': { + 'hb_bidder': 'luponmedia', + 'hb_pb': '0.40', + 'hb_size': '300x250' + }, + 'type': 'banner' + } + } + } + ], + 'seat': 'luponmedia' + } + ], + 'cur': 'USD', + 'ext': { + 'responsetimemillis': { + 'luponmedia': 233 + }, + 'tmaxrequest': 1500, + 'usersyncs': { + 'status': 'ok', + 'bidder_status': [] + } + } + }; + + let expectedResponse = [ + { + 'requestId': '2a122246ef72ea', + 'cpm': '0.43', + 'width': 300, + 'height': 250, + 'creativeId': '443801010', + 'currency': 'USD', + 'dealId': '23425', + 'netRevenue': false, + 'ttl': 300, + 'referrer': '', + 'ad': ' ' + } + ]; + + let bidderRequest = { + 'data': '{"site":{"page":"https://novi.ba/clanak/176067/fast-car-beginner-s-guide-to-tuning-turbo-engines"}}' + }; + + let result = spec.interpretResponse({ body: response }, bidderRequest); + expect(Object.keys(result[0])).to.have.members(Object.keys(expectedResponse[0])); + }); + + it('handles nobid responses', function () { + let noBidResponse = []; + + let noBidBidderRequest = { + 'data': '{"site":{"page":""}}' + } + let noBidResult = spec.interpretResponse({ body: noBidResponse }, noBidBidderRequest); + expect(noBidResult.length).to.equal(0); + }); + }); + + describe('getUserSyncs', function () { + const bidResponse1 = { + 'body': { + 'ext': { + 'responsetimemillis': { + 'luponmedia': 233 + }, + 'tmaxrequest': 1500, + 'usersyncs': { + 'status': 'ok', + 'bidder_status': [ + { + 'bidder': 'luponmedia', + 'no_cookie': true, + 'usersync': { + 'url': 'https://adxpremium.services/api/usersync', + 'type': 'redirect' + } + }, + { + 'bidder': 'luponmedia', + 'no_cookie': true, + 'usersync': { + 'url': 'https://adxpremium.services/api/iframeusersync', + 'type': 'iframe' + } + } + ] + } + } + } + }; + + const bidResponse2 = { + 'body': { + 'ext': { + 'responsetimemillis': { + 'luponmedia': 233 + }, + 'tmaxrequest': 1500, + 'usersyncs': { + 'status': 'no_cookie', + 'bidder_status': [] + } + } + } + }; + + it('should use a sync url from first response (pixel and iframe)', function () { + const syncs = spec.getUserSyncs({ pixelEnabled: true, iframeEnabled: true }, [bidResponse1, bidResponse2]); + expect(syncs).to.deep.equal([ + { + type: 'image', + url: 'https://adxpremium.services/api/usersync' + }, + { + type: 'iframe', + url: 'https://adxpremium.services/api/iframeusersync' + } + ]); + }); + + it('handle empty response (e.g. timeout)', function () { + const syncs = spec.getUserSyncs({ pixelEnabled: true, iframeEnabled: true }, []); + expect(syncs).to.deep.equal([]); + }); + + it('returns empty syncs when not pixel enabled and not iframe enabled', function () { + const syncs = spec.getUserSyncs({ pixelEnabled: false, iframeEnabled: false }, [bidResponse1]); + expect(syncs).to.deep.equal([]); + }); + + it('returns pixel syncs when pixel enabled and not iframe enabled', function() { + resetUserSync(); + + const syncs = spec.getUserSyncs({ pixelEnabled: true, iframeEnabled: false }, [bidResponse1]); + expect(syncs).to.deep.equal([ + { + type: 'image', + url: 'https://adxpremium.services/api/usersync' + } + ]); + }); + + it('returns iframe syncs when not pixel enabled and iframe enabled', function() { + resetUserSync(); + + const syncs = spec.getUserSyncs({ pixelEnabled: false, iframeEnabled: true }, [bidResponse1]); + expect(syncs).to.deep.equal([ + { + type: 'iframe', + url: 'https://adxpremium.services/api/iframeusersync' + } + ]); + }); + }); + + describe('hasValidSupplyChainParams', function () { + it('returns true if schain is valid', function () { + const schain = { + 'ver': '1.0', + 'complete': 1, + 'nodes': [ + { + 'asi': 'novi.ba', + 'sid': '199424', + 'hp': 1 + } + ] + }; + + const checkSchain = hasValidSupplyChainParams(schain); + expect(checkSchain).to.equal(true); + }); + + it('returns false if schain is invalid', function () { + const schain = { + 'ver': '1.0', + 'complete': 1, + 'nodes': [ + { + 'invalid': 'novi.ba' + } + ] + }; + + const checkSchain = hasValidSupplyChainParams(schain); + expect(checkSchain).to.equal(false); + }); + }); + + describe('onBidWon', function () { + const bidWonEvent = { + 'bidderCode': 'luponmedia', + 'width': 300, + 'height': 250, + 'statusMessage': 'Bid available', + 'adId': '105bbf8c54453ff', + 'requestId': '934b8752185955', + 'mediaType': 'banner', + 'source': 'client', + 'cpm': 0.364, + 'creativeId': '443801010', + 'currency': 'USD', + 'netRevenue': false, + 'ttl': 300, + 'referrer': '', + 'ad': '', + 'auctionId': '926a8ea3-3dd4-4bf2-95ab-c85c2ce7e99b', + 'responseTimestamp': 1598527728026, + 'requestTimestamp': 1598527727629, + 'bidder': 'luponmedia', + 'adUnitCode': 'div-gpt-ad-1533155193780-5', + 'timeToRespond': 397, + 'size': '300x250', + 'status': 'rendered' + }; + + let ajaxStub; + + beforeEach(() => { + ajaxStub = sinon.stub(spec, 'sendWinningsToServer') + }) + + afterEach(() => { + ajaxStub.restore() + }) + + it('calls luponmedia\'s callback endpoint', () => { + const result = spec.onBidWon(bidWonEvent); + expect(result).to.equal(undefined); + expect(ajaxStub.calledOnce).to.equal(true); + expect(ajaxStub.firstCall.args[0]).to.deep.equal(JSON.stringify(bidWonEvent)); + }); + }); +}); From 3f916e505f77c98ef95febebdd97f8510f384263 Mon Sep 17 00:00:00 2001 From: Chris Huie Date: Fri, 29 Oct 2021 01:52:35 -0700 Subject: [PATCH 02/17] manually try to kick off circleci tests --- modules/luponmediaBidAdapter.js | 1 + 1 file changed, 1 insertion(+) diff --git a/modules/luponmediaBidAdapter.js b/modules/luponmediaBidAdapter.js index 84ff0add012..3bf9c0def9a 100755 --- a/modules/luponmediaBidAdapter.js +++ b/modules/luponmediaBidAdapter.js @@ -106,6 +106,7 @@ var sizeMap = { 552: '300x200', 558: '640x640' }; + _each(sizeMap, (item, key) => sizeMap[item] = key); export const spec = { From ff57751ba2107ec5dda02c938abf0c75c715ed89 Mon Sep 17 00:00:00 2001 From: Chris Huie Date: Tue, 2 Nov 2021 10:43:22 -0700 Subject: [PATCH 03/17] kick off tests From aa440eddc888a08e7a5bb5fc2fa026729c0ba0e1 Mon Sep 17 00:00:00 2001 From: Chris Huie Date: Wed, 10 Nov 2021 09:14:01 -0700 Subject: [PATCH 04/17] kick off tests --- modules/luponmediaBidAdapter.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/luponmediaBidAdapter.js b/modules/luponmediaBidAdapter.js index 3bf9c0def9a..a545b911184 100755 --- a/modules/luponmediaBidAdapter.js +++ b/modules/luponmediaBidAdapter.js @@ -552,7 +552,7 @@ function parseSizes(bid, mediaType) { return size; } - // deprecated: temp legacy support + // Deprecated: temp legacy support let sizes = []; if (Array.isArray(params.sizes)) { sizes = params.sizes; From bf77bb08542b17fbab12eb943c31cd1972aa94ea Mon Sep 17 00:00:00 2001 From: adxpremium <55161519+adxpremium@users.noreply.github.com> Date: Tue, 16 Nov 2021 10:04:27 +0100 Subject: [PATCH 05/17] Update luponmediaBidAdapter.js using isFn from utils --- modules/luponmediaBidAdapter.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/modules/luponmediaBidAdapter.js b/modules/luponmediaBidAdapter.js index a545b911184..897dc3c8825 100755 --- a/modules/luponmediaBidAdapter.js +++ b/modules/luponmediaBidAdapter.js @@ -1,4 +1,4 @@ -import {isArray, logMessage, deepAccess, logWarn, parseSizesInput, deepSetValue, generateUUID, isEmpty, logError, _each} from '../src/utils.js'; +import {isArray, logMessage, deepAccess, logWarn, parseSizesInput, deepSetValue, generateUUID, isEmpty, logError, _each, isFn} from '../src/utils.js'; import {registerBidder} from '../src/adapters/bidderFactory.js'; import {config} from '../src/config.js'; import {BANNER} from '../src/mediaTypes.js'; @@ -317,7 +317,7 @@ function newOrtbBidRequest(bidRequest, bidderRequest, currentImps) { } let bidFloor; - if (typeof bidRequest.getFloor === 'function' && !config.getConfig('disableFloors')) { + if (isFn(bidRequest.getFloor) && !config.getConfig('disableFloors')) { let floorInfo; try { floorInfo = bidRequest.getFloor({ From e14acda251f5459d0e8fc40e8ddd58c331e0e605 Mon Sep 17 00:00:00 2001 From: adxpremium <55161519+adxpremium@users.noreply.github.com> Date: Mon, 28 Feb 2022 22:08:33 +0100 Subject: [PATCH 06/17] LuponMedia user sync endpoint updated --- modules/luponmediaBidAdapter.js | 60 +++++++++++++-------------------- 1 file changed, 24 insertions(+), 36 deletions(-) diff --git a/modules/luponmediaBidAdapter.js b/modules/luponmediaBidAdapter.js index 897dc3c8825..43975640113 100755 --- a/modules/luponmediaBidAdapter.js +++ b/modules/luponmediaBidAdapter.js @@ -1,4 +1,4 @@ -import {isArray, logMessage, deepAccess, logWarn, parseSizesInput, deepSetValue, generateUUID, isEmpty, logError, _each, isFn} from '../src/utils.js'; +import {isArray, logMessage, deepAccess, logWarn, parseSizesInput, deepSetValue, generateUUID, isEmpty, logError, _each, isFn, formatQS} from '../src/utils.js'; import {registerBidder} from '../src/adapters/bidderFactory.js'; import {config} from '../src/config.js'; import {BANNER} from '../src/mediaTypes.js'; @@ -171,43 +171,31 @@ export const spec = { return bidResponses; }, getUserSyncs: function (syncOptions, responses, gdprConsent, uspConsent) { - let allUserSyncs = []; - if (!hasSynced && (syncOptions.iframeEnabled || syncOptions.pixelEnabled)) { - responses.forEach(csResp => { - if (csResp.body && csResp.body.ext && csResp.body.ext.usersyncs) { - try { - let response = csResp.body.ext.usersyncs - let bidders = response.bidder_status; - for (let synci in bidders) { - let thisSync = bidders[synci]; - if (thisSync.no_cookie) { - let url = thisSync.usersync.url; - let type = thisSync.usersync.type; - - if (!url) { - logError(`No sync url for bidder luponmedia.`); - } else if ((type === 'image' || type === 'redirect') && syncOptions.pixelEnabled) { - logMessage(`Invoking image pixel user sync for luponmedia`); - allUserSyncs.push({type: 'image', url: url}); - } else if (type == 'iframe' && syncOptions.iframeEnabled) { - logMessage(`Invoking iframe user sync for luponmedia`); - allUserSyncs.push({type: 'iframe', url: url}); - } else { - logError(`User sync type "${type}" not supported for luponmedia`); - } - } - } - } catch (e) { - logError(e); - } + if (!hasSynced && syncOptions.iframeEnabled) { + // data is only assigned if params are available to pass to syncEndpoint + let params = {}; + + if (gdprConsent) { + if (typeof gdprConsent.gdprApplies === 'boolean') { + params['gdpr'] = Number(gdprConsent.gdprApplies); } - }); - } else { - logWarn('Luponmedia: Please enable iframe/pixel based user sync.'); - } + if (typeof gdprConsent.consentString === 'string') { + params['gdpr_consent'] = gdprConsent.consentString; + } + } - hasSynced = true; - return allUserSyncs; + if (uspConsent) { + params['us_privacy'] = encodeURIComponent(uspConsent); + } + + params = Object.keys(params).length ? `?${formatQS(params)}` : ''; + + hasSynced = true; + return { + type: 'iframe', + url: `https://user-sync.adxpremium.services/load-cookie.html` + params + }; + } }, onBidWon: bid => { const bidString = JSON.stringify(bid); From a3b0d1fd159df753ef605d4753db907188030884 Mon Sep 17 00:00:00 2001 From: adxpremium <55161519+adxpremium@users.noreply.github.com> Date: Tue, 1 Mar 2022 10:18:42 +0100 Subject: [PATCH 07/17] Update luponmediaBidAdapter.js --- modules/luponmediaBidAdapter.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/luponmediaBidAdapter.js b/modules/luponmediaBidAdapter.js index 43975640113..4bf1641fb30 100755 --- a/modules/luponmediaBidAdapter.js +++ b/modules/luponmediaBidAdapter.js @@ -1,4 +1,4 @@ -import {isArray, logMessage, deepAccess, logWarn, parseSizesInput, deepSetValue, generateUUID, isEmpty, logError, _each, isFn, formatQS} from '../src/utils.js'; +import {isArray, deepAccess, logWarn, parseSizesInput, deepSetValue, generateUUID, isEmpty, logError, _each, isFn, formatQS} from '../src/utils.js'; import {registerBidder} from '../src/adapters/bidderFactory.js'; import {config} from '../src/config.js'; import {BANNER} from '../src/mediaTypes.js'; From 698c54bfaca9e1375e15af6ee4256a1df2f2d4d4 Mon Sep 17 00:00:00 2001 From: adxpremium <55161519+adxpremium@users.noreply.github.com> Date: Tue, 1 Mar 2022 10:19:13 +0100 Subject: [PATCH 08/17] Update luponmediaBidAdapter_spec.js --- .../spec/modules/luponmediaBidAdapter_spec.js | 185 ++++++++++-------- 1 file changed, 103 insertions(+), 82 deletions(-) diff --git a/test/spec/modules/luponmediaBidAdapter_spec.js b/test/spec/modules/luponmediaBidAdapter_spec.js index 8aeecc87c98..ef1f8335f30 100755 --- a/test/spec/modules/luponmediaBidAdapter_spec.js +++ b/test/spec/modules/luponmediaBidAdapter_spec.js @@ -234,100 +234,121 @@ describe('luponmediaBidAdapter', function () { }); }); - describe('getUserSyncs', function () { - const bidResponse1 = { - 'body': { - 'ext': { - 'responsetimemillis': { - 'luponmedia': 233 - }, - 'tmaxrequest': 1500, - 'usersyncs': { - 'status': 'ok', - 'bidder_status': [ - { - 'bidder': 'luponmedia', - 'no_cookie': true, - 'usersync': { - 'url': 'https://adxpremium.services/api/usersync', - 'type': 'redirect' - } - }, - { - 'bidder': 'luponmedia', - 'no_cookie': true, - 'usersync': { - 'url': 'https://adxpremium.services/api/iframeusersync', - 'type': 'iframe' - } - } - ] - } - } - } - }; + describe('get user sync', function () { + const syncUrl = 'https://user-sync.adxpremium.services/load-cookie.html'; - const bidResponse2 = { - 'body': { - 'ext': { - 'responsetimemillis': { - 'luponmedia': 233 - }, - 'tmaxrequest': 1500, - 'usersyncs': { - 'status': 'no_cookie', - 'bidder_status': [] - } - } - } - }; + beforeEach(function () { + resetUserSync(); + }); - it('should use a sync url from first response (pixel and iframe)', function () { - const syncs = spec.getUserSyncs({ pixelEnabled: true, iframeEnabled: true }, [bidResponse1, bidResponse2]); - expect(syncs).to.deep.equal([ - { - type: 'image', - url: 'https://adxpremium.services/api/usersync' - }, - { - type: 'iframe', - url: 'https://adxpremium.services/api/iframeusersync' - } - ]); + it('should register the LuponMedia iframe', function () { + let syncs = spec.getUserSyncs({ + iframeEnabled: true + }); + + expect(syncs).to.deep.equal({type: 'iframe', url: syncUrl}); }); - it('handle empty response (e.g. timeout)', function () { - const syncs = spec.getUserSyncs({ pixelEnabled: true, iframeEnabled: true }, []); - expect(syncs).to.deep.equal([]); + it('should not register the LuponMedia iframe more than once', function () { + let syncs = spec.getUserSyncs({ + iframeEnabled: true + }); + expect(syncs).to.deep.equal({type: 'iframe', url: syncUrl}); + + // when called again, should still have only been called once + syncs = spec.getUserSyncs(); + expect(syncs).to.equal(undefined); }); - it('returns empty syncs when not pixel enabled and not iframe enabled', function () { - const syncs = spec.getUserSyncs({ pixelEnabled: false, iframeEnabled: false }, [bidResponse1]); - expect(syncs).to.deep.equal([]); + it('should pass gdpr params if consent is true', function () { + expect(spec.getUserSyncs({iframeEnabled: true}, {}, { + gdprApplies: true, consentString: 'foo' + })).to.deep.equal({ + type: 'iframe', url: `${syncUrl}?gdpr=1&gdpr_consent=foo` + }); }); - it('returns pixel syncs when pixel enabled and not iframe enabled', function() { - resetUserSync(); + it('should pass gdpr params if consent is false', function () { + expect(spec.getUserSyncs({iframeEnabled: true}, {}, { + gdprApplies: false, consentString: 'foo' + })).to.deep.equal({ + type: 'iframe', url: `${syncUrl}?gdpr=0&gdpr_consent=foo` + }); + }); - const syncs = spec.getUserSyncs({ pixelEnabled: true, iframeEnabled: false }, [bidResponse1]); - expect(syncs).to.deep.equal([ - { - type: 'image', - url: 'https://adxpremium.services/api/usersync' - } - ]); + it('should pass gdpr param gdpr_consent only when gdprApplies is undefined', function () { + expect(spec.getUserSyncs({iframeEnabled: true}, {}, { + consentString: 'foo' + })).to.deep.equal({ + type: 'iframe', url: `${syncUrl}?gdpr_consent=foo` + }); }); - it('returns iframe syncs when not pixel enabled and iframe enabled', function() { - resetUserSync(); + it('should pass no params if gdpr consentString is not defined', function () { + expect(spec.getUserSyncs({iframeEnabled: true}, {}, {})).to.deep.equal({ + type: 'iframe', url: `${syncUrl}` + }); + }); - const syncs = spec.getUserSyncs({ pixelEnabled: false, iframeEnabled: true }, [bidResponse1]); - expect(syncs).to.deep.equal([ - { - type: 'iframe', - url: 'https://adxpremium.services/api/iframeusersync' - } - ]); + it('should pass no params if gdpr consentString is a number', function () { + expect(spec.getUserSyncs({iframeEnabled: true}, {}, { + consentString: 0 + })).to.deep.equal({ + type: 'iframe', url: `${syncUrl}` + }); + }); + + it('should pass no params if gdpr consentString is null', function () { + expect(spec.getUserSyncs({iframeEnabled: true}, {}, { + consentString: null + })).to.deep.equal({ + type: 'iframe', url: `${syncUrl}` + }); + }); + + it('should pass no params if gdpr consentString is a object', function () { + expect(spec.getUserSyncs({iframeEnabled: true}, {}, { + consentString: {} + })).to.deep.equal({ + type: 'iframe', url: `${syncUrl}` + }); + }); + + it('should pass no params if gdpr is not defined', function () { + expect(spec.getUserSyncs({iframeEnabled: true}, {}, undefined)).to.deep.equal({ + type: 'iframe', url: `${syncUrl}` + }); + }); + + it('should pass us_privacy if uspConsent is defined', function () { + expect(spec.getUserSyncs({iframeEnabled: true}, {}, undefined, '1NYN')).to.deep.equal({ + type: 'iframe', url: `${syncUrl}?us_privacy=1NYN` + }); + }); + + it('should pass us_privacy after gdpr if both are present', function () { + expect(spec.getUserSyncs({iframeEnabled: true}, {}, { + consentString: 'foo' + }, '1NYN')).to.deep.equal({ + type: 'iframe', url: `${syncUrl}?gdpr_consent=foo&us_privacy=1NYN` + }); + }); + + it('should pass gdprApplies', function () { + expect(spec.getUserSyncs({iframeEnabled: true}, {}, { + gdprApplies: true + }, '1NYN')).to.deep.equal({ + type: 'iframe', url: `${syncUrl}?gdpr=1&us_privacy=1NYN` + }); + }); + + it('should pass all correctly', function () { + expect(spec.getUserSyncs({iframeEnabled: true}, {}, { + gdprApplies: true, + consentString: 'foo' + }, '1NYN')).to.deep.equal({ + type: 'iframe', url: `${syncUrl}?gdpr=1&gdpr_consent=foo&us_privacy=1NYN` + }); }); }); From 7ca1098e7fbbea3257b92f5e57321254e0d03ba4 Mon Sep 17 00:00:00 2001 From: adxpremium <55161519+adxpremium@users.noreply.github.com> Date: Wed, 13 Apr 2022 10:45:08 +0200 Subject: [PATCH 09/17] Update luponmediaBidAdapter - Digitrust removed Digitrust references --- modules/luponmediaBidAdapter.js | 43 --------------------------------- 1 file changed, 43 deletions(-) diff --git a/modules/luponmediaBidAdapter.js b/modules/luponmediaBidAdapter.js index 4bf1641fb30..23743d13aa7 100755 --- a/modules/luponmediaBidAdapter.js +++ b/modules/luponmediaBidAdapter.js @@ -7,13 +7,6 @@ import { ajax } from '../src/ajax.js'; const BIDDER_CODE = 'luponmedia'; const ENDPOINT_URL = 'https://rtb.adxpremium.services/openrtb2/auction'; -const DIGITRUST_PROP_NAMES = { - PREBID_SERVER: { - id: 'id', - keyv: 'keyv' - } -}; - var sizeMap = { 1: '468x60', 2: '728x90', @@ -326,10 +319,6 @@ function newOrtbBidRequest(bidRequest, bidderRequest, currentImps) { appendSiteAppDevice(data, bidRequest, bidderRequest); - const digiTrust = _getDigiTrustQueryParams(bidRequest, 'PREBID_SERVER'); - if (digiTrust) { - deepSetValue(data, 'user.ext.digitrust', digiTrust); - } if (bidderRequest.gdprConsent) { // note - gdprApplies & consentString may be undefined in certain use-cases for consentManagement module @@ -449,38 +438,6 @@ function newOrtbBidRequest(bidRequest, bidderRequest, currentImps) { return data; } -function _getDigiTrustQueryParams(bidRequest = {}, endpointName) { - if (!endpointName || !DIGITRUST_PROP_NAMES[endpointName]) { - return null; - } - const propNames = DIGITRUST_PROP_NAMES[endpointName]; - - function getDigiTrustId() { - const bidRequestDigitrust = deepAccess(bidRequest, 'userId.digitrustid.data'); - if (bidRequestDigitrust) { - return bidRequestDigitrust; - } - - let digiTrustUser = (window.DigiTrust && (config.getConfig('digiTrustId') || window.DigiTrust.getUser({member: 'T9QSFKPDN9'}))); - return (digiTrustUser && digiTrustUser.success && digiTrustUser.identity) || null; - } - - let digiTrustId = getDigiTrustId(); - // Verify there is an ID and this user has not opted out - if (!digiTrustId || (digiTrustId.privacy && digiTrustId.privacy.optout)) { - return null; - } - - const digiTrustQueryParams = { - [propNames.id]: digiTrustId.id, - [propNames.keyv]: digiTrustId.keyv - }; - if (propNames.pref) { - digiTrustQueryParams[propNames.pref] = 0; - } - return digiTrustQueryParams; -} - function _getPageUrl(bidRequest, bidderRequest) { let pageUrl = config.getConfig('pageUrl'); if (bidRequest.params.referrer) { From b176342281d27c38889bdac6bffd6ab76092b14c Mon Sep 17 00:00:00 2001 From: adxpremium <55161519+adxpremium@users.noreply.github.com> Date: Wed, 13 Apr 2022 11:11:10 +0200 Subject: [PATCH 10/17] Update luponmediaBidAdapter.js --- modules/luponmediaBidAdapter.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/luponmediaBidAdapter.js b/modules/luponmediaBidAdapter.js index 23743d13aa7..d9004c2c2c3 100755 --- a/modules/luponmediaBidAdapter.js +++ b/modules/luponmediaBidAdapter.js @@ -163,7 +163,7 @@ export const spec = { } return bidResponses; }, - getUserSyncs: function (syncOptions, responses, gdprConsent, uspConsent) { + getUserSyncs: function (syncOptions, _responses, gdprConsent, uspConsent) { if (!hasSynced && syncOptions.iframeEnabled) { // data is only assigned if params are available to pass to syncEndpoint let params = {}; From b8145bc25b6cf3ce267e8a5fb962cc73c49055d4 Mon Sep 17 00:00:00 2001 From: adxpremium <55161519+adxpremium@users.noreply.github.com> Date: Wed, 13 Apr 2022 14:24:51 +0200 Subject: [PATCH 11/17] Update luponmediaBidAdapter.js --- modules/luponmediaBidAdapter.js | 186 ++++++++++++++++++-------------- 1 file changed, 106 insertions(+), 80 deletions(-) diff --git a/modules/luponmediaBidAdapter.js b/modules/luponmediaBidAdapter.js index d9004c2c2c3..323293b5223 100755 --- a/modules/luponmediaBidAdapter.js +++ b/modules/luponmediaBidAdapter.js @@ -297,29 +297,32 @@ function newOrtbBidRequest(bidRequest, bidderRequest, currentImps) { } } - let bidFloor; - if (isFn(bidRequest.getFloor) && !config.getConfig('disableFloors')) { - let floorInfo; - try { - floorInfo = bidRequest.getFloor({ - currency: 'USD', - mediaType: 'video', - size: parseSizes(bidRequest, 'video') - }); - } catch (e) { - logError('LuponMedia: getFloor threw an error: ', e); - } - bidFloor = typeof floorInfo === 'object' && floorInfo.currency === 'USD' && !isNaN(parseInt(floorInfo.floor)) ? parseFloat(floorInfo.floor) : undefined; - } else { - bidFloor = parseFloat(deepAccess(bidRequest, 'params.floor')); + setBidFloor(bidRequest, data); + + appendSiteAppDevice(data, bidRequest, bidderRequest); + + setGdprAndPrivacy(bidderRequest, data); + setUserId(bidRequest, data); + + if (config.getConfig('coppa') === true) { + deepSetValue(data, 'regs.coppa', 1); } - if (!isNaN(bidFloor)) { - data.imp[0].bidfloor = bidFloor; + + if (bidRequest.schain && hasValidSupplyChainParams(bidRequest.schain)) { + deepSetValue(data, 'source.ext.schain', bidRequest.schain); } - appendSiteAppDevice(data, bidRequest, bidderRequest); + setSiteAndUserData(bidRequest, bidderRequest, data); + const pbAdSlot = deepAccess(bidRequest, 'fpd.context.pbAdSlot'); + if (typeof pbAdSlot === 'string' && pbAdSlot) { + deepSetValue(data.imp[0].ext, 'context.data.adslot', pbAdSlot); + } + return data; +} + +function setGdprAndPrivacy(bidderRequest, data) { if (bidderRequest.gdprConsent) { // note - gdprApplies & consentString may be undefined in certain use-cases for consentManagement module let gdprApplies; @@ -334,7 +337,9 @@ function newOrtbBidRequest(bidRequest, bidderRequest, currentImps) { if (bidderRequest.uspConsent) { deepSetValue(data, 'regs.ext.us_privacy', bidderRequest.uspConsent); } +} +function setUserId(bidRequest, data) { // Set user uuid deepSetValue(data, 'user.id', generateUUID()); @@ -346,68 +351,18 @@ function newOrtbBidRequest(bidRequest, bidderRequest, currentImps) { } if (bidRequest.userId && typeof bidRequest.userId === 'object' && - (bidRequest.userId.tdid || bidRequest.userId.pubcid || bidRequest.userId.lipb || bidRequest.userId.idl_env)) { - deepSetValue(data, 'user.ext.eids', []); - - if (bidRequest.userId.tdid) { - data.user.ext.eids.push({ - source: 'adserver.org', - uids: [{ - id: bidRequest.userId.tdid, - ext: { - rtiPartner: 'TDID' - } - }] - }); - } + (bidRequest.userId.tdid || bidRequest.userId.pubcid || bidRequest.userId.lipb || bidRequest.userId.idl_env)) { - if (bidRequest.userId.pubcid) { - data.user.ext.eids.push({ - source: 'pubcommon', - uids: [{ - id: bidRequest.userId.pubcid, - }] - }); - } - - // support liveintent ID - if (bidRequest.userId.lipb && bidRequest.userId.lipb.lipbid) { - data.user.ext.eids.push({ - source: 'liveintent.com', - uids: [{ - id: bidRequest.userId.lipb.lipbid - }] - }); - - data.user.ext.tpid = { - source: 'liveintent.com', - uid: bidRequest.userId.lipb.lipbid - }; - - if (Array.isArray(bidRequest.userId.lipb.segments) && bidRequest.userId.lipb.segments.length) { - deepSetValue(data, 'rp.target.LIseg', bidRequest.userId.lipb.segments); - } - } - - // support identityLink (aka LiveRamp) - if (bidRequest.userId.idl_env) { - data.user.ext.eids.push({ - source: 'liveramp.com', - uids: [{ - id: bidRequest.userId.idl_env - }] - }); - } - } - - if (config.getConfig('coppa') === true) { - deepSetValue(data, 'regs.coppa', 1); - } + deepSetValue(data, 'user.ext.eids', []); + setAdserverOrg(bidRequest, data); + setPubcommon(bidRequest, data); + setLiveIntent(bidRequest, data); + setIdentityLink(bidRequest, data); - if (bidRequest.schain && hasValidSupplyChainParams(bidRequest.schain)) { - deepSetValue(data, 'source.ext.schain', bidRequest.schain); } +} +function setSiteAndUserData(bidRequest, bidderRequest, data) { const siteData = Object.assign({}, bidRequest.params.inventory, config.getConfig('fpd.context')); const userData = Object.assign({}, bidRequest.params.visitor, config.getConfig('fpd.user')); @@ -429,13 +384,84 @@ function newOrtbBidRequest(bidRequest, bidderRequest, currentImps) { deepSetValue(data, 'ext.prebid.bidderconfig.0', bidderData); } +} - const pbAdSlot = deepAccess(bidRequest, 'fpd.context.pbAdSlot'); - if (typeof pbAdSlot === 'string' && pbAdSlot) { - deepSetValue(data.imp[0].ext, 'context.data.adslot', pbAdSlot); +function setAdserverOrg(bidRequest, data) { + if (bidRequest.userId.tdid) { + data.user.ext.eids.push({ + source: 'adserver.org', + uids: [{ + id: bidRequest.userId.tdid, + ext: { + rtiPartner: 'TDID' + } + }] + }); } +} - return data; +function setLiveIntent(bidRequest, data) { + if (bidRequest.userId.lipb && bidRequest.userId.lipb.lipbid) { + data.user.ext.eids.push({ + source: 'liveintent.com', + uids: [{ + id: bidRequest.userId.lipb.lipbid + }] + }); + + data.user.ext.tpid = { + source: 'liveintent.com', + uid: bidRequest.userId.lipb.lipbid + }; + + if (Array.isArray(bidRequest.userId.lipb.segments) && bidRequest.userId.lipb.segments.length) { + deepSetValue(data, 'rp.target.LIseg', bidRequest.userId.lipb.segments); + } + } +} + +function setIdentityLink(bidRequest, data) { + if (bidRequest.userId.idl_env) { + data.user.ext.eids.push({ + source: 'liveramp.com', + uids: [{ + id: bidRequest.userId.idl_env + }] + }); + } +} + +function setPubcommon(bidRequest, data) { + if (bidRequest.userId.pubcid) { + data.user.ext.eids.push({ + source: 'pubcommon', + uids: [{ + id: bidRequest.userId.pubcid, + }] + }); + } +} + +function setBidFloor(bidRequest, data) { + let bidFloor; + if (isFn(bidRequest.getFloor) && !config.getConfig('disableFloors')) { + let floorInfo; + try { + floorInfo = bidRequest.getFloor({ + currency: 'USD', + mediaType: 'video', + size: parseSizes(bidRequest, 'video') + }); + } catch (e) { + logError('LuponMedia: getFloor threw an error: ', e); + } + bidFloor = typeof floorInfo === 'object' && floorInfo.currency === 'USD' && !isNaN(parseInt(floorInfo.floor)) ? parseFloat(floorInfo.floor) : undefined; + } else { + bidFloor = parseFloat(deepAccess(bidRequest, 'params.floor')); + } + if (!isNaN(bidFloor)) { + data.imp[0].bidfloor = bidFloor; + } } function _getPageUrl(bidRequest, bidderRequest) { From 1b3ca91afc11886a6bfce192fd2b03a03193f062 Mon Sep 17 00:00:00 2001 From: adxpremium <55161519+adxpremium@users.noreply.github.com> Date: Wed, 13 Apr 2022 14:38:47 +0200 Subject: [PATCH 12/17] Update luponmediaBidAdapter.js --- modules/luponmediaBidAdapter.js | 2 -- 1 file changed, 2 deletions(-) diff --git a/modules/luponmediaBidAdapter.js b/modules/luponmediaBidAdapter.js index 323293b5223..1335cddce5a 100755 --- a/modules/luponmediaBidAdapter.js +++ b/modules/luponmediaBidAdapter.js @@ -352,13 +352,11 @@ function setUserId(bidRequest, data) { if (bidRequest.userId && typeof bidRequest.userId === 'object' && (bidRequest.userId.tdid || bidRequest.userId.pubcid || bidRequest.userId.lipb || bidRequest.userId.idl_env)) { - deepSetValue(data, 'user.ext.eids', []); setAdserverOrg(bidRequest, data); setPubcommon(bidRequest, data); setLiveIntent(bidRequest, data); setIdentityLink(bidRequest, data); - } } From 1bdc194a7e153ec3175c0563699ac37214b86c44 Mon Sep 17 00:00:00 2001 From: adxpremium <55161519+adxpremium@users.noreply.github.com> Date: Thu, 12 May 2022 10:39:13 +0200 Subject: [PATCH 13/17] Update luponmediaBidAdapter.js Removed FPD legacy --- modules/luponmediaBidAdapter.js | 114 ++++++++++++++++++++++++-------- 1 file changed, 85 insertions(+), 29 deletions(-) diff --git a/modules/luponmediaBidAdapter.js b/modules/luponmediaBidAdapter.js index 1335cddce5a..5edb52c4b31 100755 --- a/modules/luponmediaBidAdapter.js +++ b/modules/luponmediaBidAdapter.js @@ -312,12 +312,7 @@ function newOrtbBidRequest(bidRequest, bidderRequest, currentImps) { deepSetValue(data, 'source.ext.schain', bidRequest.schain); } - setSiteAndUserData(bidRequest, bidderRequest, data); - - const pbAdSlot = deepAccess(bidRequest, 'fpd.context.pbAdSlot'); - if (typeof pbAdSlot === 'string' && pbAdSlot) { - deepSetValue(data.imp[0].ext, 'context.data.adslot', pbAdSlot); - } + setSiteAndUserData(bidRequest, BANNER, data); return data; } @@ -360,29 +355,6 @@ function setUserId(bidRequest, data) { } } -function setSiteAndUserData(bidRequest, bidderRequest, data) { - const siteData = Object.assign({}, bidRequest.params.inventory, config.getConfig('fpd.context')); - const userData = Object.assign({}, bidRequest.params.visitor, config.getConfig('fpd.user')); - - if (!isEmpty(siteData) || !isEmpty(userData)) { - const bidderData = { - bidders: [ bidderRequest.bidderCode ], - config: { - fpd: {} - } - }; - - if (!isEmpty(siteData)) { - bidderData.config.fpd.site = siteData; - } - - if (!isEmpty(userData)) { - bidderData.config.fpd.user = userData; - } - - deepSetValue(data, 'ext.prebid.bidderconfig.0', bidderData); - } -} function setAdserverOrg(bidRequest, data) { if (bidRequest.userId.tdid) { @@ -536,4 +508,88 @@ function parseSizes(bid, mediaType) { return masSizeOrdering(sizes); } +function setSiteAndUserData(bidRequest, mediaType, data) { + const BID_FPD = { + user: {ext: {data: {...bidRequest.params.visitor}}}, + site: {ext: {data: {...bidRequest.params.inventory}}} + }; + + if (bidRequest.params.keywords) BID_FPD.site.keywords = (isArray(bidRequest.params.keywords)) ? bidRequest.params.keywords.join(',') : bidRequest.params.keywords; + + let fpd = mergeDeep({}, config.getConfig('ortb2') || {}, BID_FPD); + let impData = deepAccess(bidRequest.ortb2Imp, 'ext.data') || {}; + + const gpid = deepAccess(bidRequest, 'ortb2Imp.ext.gpid'); + const SEGTAX = {user: [4], site: [1, 2, 5, 6]}; + const MAP = {user: 'tg_v.', site: 'tg_i.', adserver: 'tg_i.dfp_ad_unit_code', pbadslot: 'tg_i.pbadslot', keywords: 'kw'}; + const validate = function(prop, key, parentName) { + if (key === 'data' && Array.isArray(prop)) { + return prop.filter(name => name.segment && deepAccess(name, 'ext.segtax') && SEGTAX[parentName] && + SEGTAX[parentName].indexOf(deepAccess(name, 'ext.segtax')) !== -1).map(value => { + let segments = value.segment.filter(obj => obj.id).reduce((result, obj) => { + result.push(obj.id); + return result; + }, []); + if (segments.length > 0) return segments.toString(); + }).toString(); + } else if (typeof prop === 'object' && !Array.isArray(prop)) { + logWarn('LuponMedia: Filtered FPD key: ', key, ': Expected value to be string, integer, or an array of strings/ints'); + } else if (typeof prop !== 'undefined') { + return (Array.isArray(prop)) ? prop.filter(value => { + if (typeof value !== 'object' && typeof value !== 'undefined') return value.toString(); + + logWarn('LuponMedia: Filtered value: ', value, 'for key', key, ': Expected value to be string, integer, or an array of strings/ints'); + }).toString() : prop.toString(); + } + }; + const addBannerData = function(obj, name, key, isParent = true) { + let val = validate(obj, key, name); + let loc = (MAP[key] && isParent) ? `${MAP[key]}` : (key === 'data') ? `${MAP[name]}iab` : `${MAP[name]}${key}`; + data[loc] = (data[loc]) ? data[loc].concat(',', val) : val; + } + + if (mediaType === BANNER) { + ['site', 'user'].forEach(name => { + Object.keys(fpd[name]).forEach((key) => { + if (name === 'site' && key === 'content' && fpd[name][key].data) { + addBannerData(fpd[name][key].data, name, 'data'); + } else if (key !== 'ext') { + addBannerData(fpd[name][key], name, key); + } else if (fpd[name][key].data) { + Object.keys(fpd[name].ext.data).forEach((key) => { + addBannerData(fpd[name].ext.data[key], name, key, false); + }); + } + }); + }); + Object.keys(impData).forEach((key) => { + if (key !== 'adserver') { + addBannerData(impData[key], 'site', key); + } else if (impData[key].name === 'gam') { + addBannerData(impData[key].adslot, name, key) + } + }); + + // add in gpid + if (gpid) { + data['p_gpid'] = gpid; + } + + // only send one of pbadslot or dfp adunit code (prefer pbadslot) + if (data['tg_i.pbadslot']) { + delete data['tg_i.dfp_ad_unit_code']; + } + } else { + if (Object.keys(impData).length) { + mergeDeep(data.imp[0].ext, {data: impData}); + } + // add in gpid + if (gpid) { + data.imp[0].ext.gpid = gpid; + } + + mergeDeep(data, fpd); + } +} + registerBidder(spec); From b1d236552e570d579354a6ff7bf063aaa625a654 Mon Sep 17 00:00:00 2001 From: adxpremium <55161519+adxpremium@users.noreply.github.com> Date: Thu, 12 May 2022 11:04:14 +0200 Subject: [PATCH 14/17] Update luponmediaBidAdapter.js Fixed empty lines --- modules/luponmediaBidAdapter.js | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/modules/luponmediaBidAdapter.js b/modules/luponmediaBidAdapter.js index 5edb52c4b31..5d0cbee2972 100755 --- a/modules/luponmediaBidAdapter.js +++ b/modules/luponmediaBidAdapter.js @@ -1,4 +1,4 @@ -import {isArray, deepAccess, logWarn, parseSizesInput, deepSetValue, generateUUID, isEmpty, logError, _each, isFn, formatQS} from '../src/utils.js'; +import {isArray, deepAccess, logWarn, parseSizesInput, deepSetValue, generateUUID, mergeDeep, logError, _each, isFn, formatQS} from '../src/utils.js'; import {registerBidder} from '../src/adapters/bidderFactory.js'; import {config} from '../src/config.js'; import {BANNER} from '../src/mediaTypes.js'; @@ -355,7 +355,6 @@ function setUserId(bidRequest, data) { } } - function setAdserverOrg(bidRequest, data) { if (bidRequest.userId.tdid) { data.user.ext.eids.push({ From c37e7d4160001ba905a8252394d868451e514839 Mon Sep 17 00:00:00 2001 From: adxpremium <55161519+adxpremium@users.noreply.github.com> Date: Wed, 29 Jun 2022 11:01:43 +0200 Subject: [PATCH 15/17] Update luponmediaBidAdapter.js --- modules/luponmediaBidAdapter.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/luponmediaBidAdapter.js b/modules/luponmediaBidAdapter.js index 84d30186fa4..e44e8d1f5ea 100755 --- a/modules/luponmediaBidAdapter.js +++ b/modules/luponmediaBidAdapter.js @@ -516,7 +516,7 @@ function setSiteAndUserData(bidRequest, mediaType, data) { if (bidRequest.params.keywords) BID_FPD.site.keywords = (isArray(bidRequest.params.keywords)) ? bidRequest.params.keywords.join(',') : bidRequest.params.keywords; - let fpd = mergeDeep({}, config.getConfig('ortb2') || {}, BID_FPD); + let fpd = mergeDeep({}, bidderRequest.ortb2 || {}, BID_FPD); let impData = deepAccess(bidRequest.ortb2Imp, 'ext.data') || {}; const gpid = deepAccess(bidRequest, 'ortb2Imp.ext.gpid'); From 5bac5046f911a215ed84d1a005443e522188cce6 Mon Sep 17 00:00:00 2001 From: adxpremium <55161519+adxpremium@users.noreply.github.com> Date: Wed, 29 Jun 2022 11:05:09 +0200 Subject: [PATCH 16/17] Update luponmediaBidAdapter.js --- modules/luponmediaBidAdapter.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/luponmediaBidAdapter.js b/modules/luponmediaBidAdapter.js index e44e8d1f5ea..f0294cd43e5 100755 --- a/modules/luponmediaBidAdapter.js +++ b/modules/luponmediaBidAdapter.js @@ -516,7 +516,7 @@ function setSiteAndUserData(bidRequest, mediaType, data) { if (bidRequest.params.keywords) BID_FPD.site.keywords = (isArray(bidRequest.params.keywords)) ? bidRequest.params.keywords.join(',') : bidRequest.params.keywords; - let fpd = mergeDeep({}, bidderRequest.ortb2 || {}, BID_FPD); + let fpd = mergeDeep({}, bidRequest.ortb2 || {}, BID_FPD); let impData = deepAccess(bidRequest.ortb2Imp, 'ext.data') || {}; const gpid = deepAccess(bidRequest, 'ortb2Imp.ext.gpid'); From c52f0fb06db757705dfa9dab41f06af7f0ba186c Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 30 Jan 2023 06:07:17 +0000 Subject: [PATCH 17/17] Bump tibdex/github-app-token from 1.3.0 to 1.8.0 Bumps [tibdex/github-app-token](https://github.com/tibdex/github-app-token) from 1.3.0 to 1.8.0. - [Release notes](https://github.com/tibdex/github-app-token/releases) - [Commits](https://github.com/tibdex/github-app-token/compare/36464acb844fc53b9b8b2401da68844f6b05ebb0...b62528385c34dbc9f38e5f4225ac829252d1ea92) --- updated-dependencies: - dependency-name: tibdex/github-app-token dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- .github/workflows/issue_tracker.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/issue_tracker.yml b/.github/workflows/issue_tracker.yml index fa33ffe5c53..35c6db80c26 100644 --- a/.github/workflows/issue_tracker.yml +++ b/.github/workflows/issue_tracker.yml @@ -14,7 +14,7 @@ jobs: steps: - name: Generate token id: generate_token - uses: tibdex/github-app-token@36464acb844fc53b9b8b2401da68844f6b05ebb0 + uses: tibdex/github-app-token@b62528385c34dbc9f38e5f4225ac829252d1ea92 with: app_id: ${{ secrets.ISSUE_APP_ID }} private_key: ${{ secrets.ISSUE_APP_PEM }}