From c6433348839e15f77a3a9693a5eca935dc6f3769 Mon Sep 17 00:00:00 2001 From: vincentproxistore <56686565+vincentproxistore@users.noreply.github.com> Date: Thu, 8 Jul 2021 20:48:15 +0200 Subject: [PATCH] Proxistore Bid Adapter: support advertiserDomains (#7136) * support advertiserDomains * Revert "support advertiserDomains" This reverts commit e7488d069e24d11f3afd6f6b996506b24961fc3e. * remove package-lock.json * Revert "remove package-lock.json" This reverts commit cd9dd5df192bfea84f7cde9d3fb7a444650380dd. * Revert "Revert "support advertiserDomains"" This reverts commit b80f1c15d5f49c48781f7af958abafef83465819. * add adapter * Revert "Revert "support advertiserDomains"" This reverts commit b80f1c15d5f49c48781f7af958abafef83465819. * copy package-lock.json from prebid master --- modules/proxistoreBidAdapter.js | 204 ++++++++++++++++++ .../spec/modules/proxistoreBidAdapter_spec.js | 169 +++++++++++++++ 2 files changed, 373 insertions(+) create mode 100644 modules/proxistoreBidAdapter.js create mode 100644 test/spec/modules/proxistoreBidAdapter_spec.js diff --git a/modules/proxistoreBidAdapter.js b/modules/proxistoreBidAdapter.js new file mode 100644 index 00000000000..2e4c7eb22cb --- /dev/null +++ b/modules/proxistoreBidAdapter.js @@ -0,0 +1,204 @@ +import { registerBidder } from '../src/adapters/bidderFactory.js'; +import * as utils from '../src/utils.js'; + +const BIDDER_CODE = 'proxistore'; +const PROXISTORE_VENDOR_ID = 418; + +function _createServerRequest(bidRequests, bidderRequest) { + var sizeIds = []; + bidRequests.forEach(function (bid) { + var sizeId = { + id: bid.bidId, + sizes: bid.sizes.map(function (size) { + return { + width: size[0], + height: size[1], + }; + }), + floor: _assignFloor(bid), + segments: _assignSegments(bid), + }; + sizeIds.push(sizeId); + }); + var payload = { + auctionId: bidRequests[0].auctionId, + transactionId: bidRequests[0].auctionId, + bids: sizeIds, + website: bidRequests[0].params.website, + language: bidRequests[0].params.language, + gdpr: { + applies: false, + consentGiven: false, + }, + }; + + if (bidderRequest && bidderRequest.gdprConsent) { + var gdprConsent = bidderRequest.gdprConsent; + + if ( + typeof gdprConsent.gdprApplies === 'boolean' && + gdprConsent.gdprApplies + ) { + payload.gdpr.applies = true; + } + + if ( + typeof gdprConsent.consentString === 'string' && + gdprConsent.consentString + ) { + payload.gdpr.consentString = bidderRequest.gdprConsent.consentString; + } + + if (gdprConsent.vendorData) { + var vendorData = gdprConsent.vendorData; + var apiVersion = gdprConsent.apiVersion; + + if ( + apiVersion === 2 && + vendorData.vendor && + vendorData.vendor.consents && + typeof vendorData.vendor.consents[PROXISTORE_VENDOR_ID.toString(10)] !== + 'undefined' + ) { + payload.gdpr.consentGiven = + !!vendorData.vendor.consents[PROXISTORE_VENDOR_ID.toString(10)]; + } else if ( + apiVersion === 1 && + vendorData.vendorConsents && + typeof vendorData.vendorConsents[PROXISTORE_VENDOR_ID.toString(10)] !== + 'undefined' + ) { + payload.gdpr.consentGiven = + !!vendorData.vendorConsents[PROXISTORE_VENDOR_ID.toString(10)]; + } + } + } + + var options = { + contentType: 'application/json', + withCredentials: payload.gdpr.consentGiven, + customHeaders: { + version: '1.0.4', + }, + }; + var endPointUri = + payload.gdpr.consentGiven || !payload.gdpr.applies + ? 'https://abs.proxistore.com/'.concat( + payload.language, + '/v3/rtb/prebid/multi' + ) + : 'https://abs.cookieless-proxistore.com/'.concat( + payload.language, + '/v3/rtb/prebid/multi' + ); + return { + method: 'POST', + url: endPointUri, + data: JSON.stringify(payload), + options: options, + }; +} + +function _assignSegments(bid) { + if ( + bid.ortb2 && + bid.ortb2.user && + bid.ortb2.user.ext && + bid.ortb2.user.ext.data + ) { + return ( + bid.ortb2.user.ext.data || { + segments: [], + contextual_categories: {}, + } + ); + } + + return { + segments: [], + contextual_categories: {}, + }; +} + +function _createBidResponse(response) { + return { + requestId: response.requestId, + cpm: response.cpm, + width: response.width, + height: response.height, + ad: response.ad, + ttl: response.ttl, + creativeId: response.creativeId, + currency: response.currency, + netRevenue: response.netRevenue, + vastUrl: response.vastUrl, + vastXml: response.vastXml, + dealId: response.dealId, + meta: response.meta, + }; +} +/** + * Determines whether or not the given bid request is valid. + * + * @param bid The bid params to validate. + * @return boolean True if this is a valid bid, and false otherwise. + */ + +function isBidRequestValid(bid) { + return !!(bid.params.website && bid.params.language); +} +/** + * Make a server request from the list of BidRequests. + * + * @param bidRequests - an array of bids + * @param bidderRequest + * @return ServerRequest Info describing the request to the server. + */ + +function buildRequests(bidRequests, bidderRequest) { + var request = _createServerRequest(bidRequests, bidderRequest); + + return request; +} +/** + * Unpack the response from the server into a list of bids. + * + * @param serverResponse A successful response from the server. + * @param bidRequest Request original server request + * @return An array of bids which were nested inside the server. + */ + +function interpretResponse(serverResponse, bidRequest) { + return serverResponse.body.map(_createBidResponse); +} + +function _assignFloor(bid) { + if (!utils.isFn(bid.getFloor)) { + // eslint-disable-next-line no-console + console.log(bid.params.bidFloor); + return bid.params.bidFloor ? bid.params.bidFloor : null; + } + const floor = bid.getFloor({ + currency: 'EUR', + mediaType: 'banner', + size: '*', + }); + + if ( + utils.isPlainObject(floor) && + !isNaN(floor.floor) && + floor.currency === 'EUR' + ) { + return floor.floor; + } + return null; +} + +export const spec = { + code: BIDDER_CODE, + isBidRequestValid: isBidRequestValid, + buildRequests: buildRequests, + interpretResponse: interpretResponse, +}; + +registerBidder(spec); diff --git a/test/spec/modules/proxistoreBidAdapter_spec.js b/test/spec/modules/proxistoreBidAdapter_spec.js new file mode 100644 index 00000000000..95f29423492 --- /dev/null +++ b/test/spec/modules/proxistoreBidAdapter_spec.js @@ -0,0 +1,169 @@ +import { expect } from 'chai'; +import { spec } from 'modules/proxistoreBidAdapter.js'; +import { newBidder } from 'src/adapters/bidderFactory.js'; +import { config } from '../../../src/config.js'; + +const BIDDER_CODE = 'proxistore'; +describe('ProxistoreBidAdapter', function () { + const consentString = 'BOJ8RZsOJ8RZsABAB8AAAAAZ+A=='; + const bidderRequest = { + bidderCode: BIDDER_CODE, + auctionId: '1025ba77-5463-4877-b0eb-14b205cb9304', + bidderRequestId: '10edf38ec1a719', + gdprConsent: { + apiVersion: 2, + gdprApplies: true, + consentString: consentString, + vendorData: { + vendor: { + consents: { + 418: true, + }, + }, + }, + }, + }; + let bid = { + sizes: [[300, 600]], + params: { + website: 'example.fr', + language: 'fr', + }, + ortb2: { + user: { ext: { data: { segments: [], contextual_categories: {} } } }, + }, + auctionId: 442133079, + bidId: 464646969, + transactionId: 511916005, + }; + describe('isBidRequestValid', function () { + it('it should be true if required params are presents and there is no info in the local storage', function () { + expect(spec.isBidRequestValid(bid)).to.equal(true); + }); + it('it should be false if the value in the localstorage is less than 5minutes of the actual time', function () { + const date = new Date(); + date.setMinutes(date.getMinutes() - 1); + localStorage.setItem(`PX_NoAds_${bid.params.website}`, date); + expect(spec.isBidRequestValid(bid)).to.equal(true); + }); + it('it should be true if the value in the localstorage is more than 5minutes of the actual time', function () { + const date = new Date(); + date.setMinutes(date.getMinutes() - 10); + localStorage.setItem(`PX_NoAds_${bid.params.website}`, date); + expect(spec.isBidRequestValid(bid)).to.equal(true); + }); + }); + describe('buildRequests', function () { + const url = { + cookieBase: 'https://abs.proxistore.com/fr/v3/rtb/prebid/multi', + cookieLess: + 'https://abs.cookieless-proxistore.com/fr/v3/rtb/prebid/multi', + }; + + let request = spec.buildRequests([bid], bidderRequest); + it('should return a valid object', function () { + expect(request).to.be.an('object'); + expect(request.method).to.exist; + expect(request.url).to.exist; + expect(request.data).to.exist; + }); + it('request method should be POST', function () { + expect(request.method).to.equal('POST'); + }); + it('should have the value consentGiven to true bc we have 418 in the vendor list', function () { + const data = JSON.parse(request.data); + expect(data.gdpr.consentString).equal( + bidderRequest.gdprConsent.consentString + ); + expect(data.gdpr.applies).to.be.true; + expect(data.gdpr.consentGiven).to.be.true; + }); + it('should contain a valid url', function () { + // has gdpr consent + expect(request.url).equal(url.cookieBase); + // doens't have gpdr consent + bidderRequest.gdprConsent.vendorData = null; + + request = spec.buildRequests([bid], bidderRequest); + expect(request.url).equal(url.cookieLess); + + // api v2 + bidderRequest.gdprConsent = { + gdprApplies: true, + allowAuctionWithoutConsent: true, + consentString: consentString, + vendorData: { + vendor: { + consents: { + 418: true, + }, + }, + }, + apiVersion: 2, + }; + // has gdpr consent + request = spec.buildRequests([bid], bidderRequest); + expect(request.url).equal(url.cookieBase); + + bidderRequest.gdprConsent.vendorData.vendor = {}; + request = spec.buildRequests([bid], bidderRequest); + expect(request.url).equal(url.cookieLess); + }); + it('should have a property a length of bids equal to one if there is only one bid', function () { + const data = JSON.parse(request.data); + expect(data.hasOwnProperty('bids')).to.be.true; + expect(data.bids).to.be.an('array'); + expect(data.bids.length).equal(1); + expect(data.bids[0].hasOwnProperty('id')).to.be.true; + expect(data.bids[0].sizes).to.be.an('array'); + }); + it('should correctly set bidfloor on imp when getfloor in scope', function () { + let data = JSON.parse(request.data); + expect(data.bids[0].floor).to.be.null; + + bid.params['bidFloor'] = 1; + let req = spec.buildRequests([bid], bidderRequest); + data = JSON.parse(req.data); + // eslint-disable-next-line no-console + console.log(data.bids[0]); + expect(data.bids[0].floor).equal(1); + bid.getFloor = function () { + return { currency: 'USD', floor: 1.0 }; + }; + req = spec.buildRequests([bid], bidderRequest); + data = JSON.parse(req.data); + expect(data.bids[0].floor).to.be.null; + }); + }); + describe('interpretResponse', function () { + const emptyResponseParam = { body: [] }; + const fakeResponseParam = { + body: [ + { + ad: '', + cpm: 6.25, + creativeId: '22c3290b-8cd5-4cd6-8e8c-28a2de180ccd', + currency: 'EUR', + dealId: '2021-03_a63ec55e-b9bb-4ca4-b2c9-f456be67e656', + height: 600, + netRevenue: true, + requestId: '3543724f2a033c9', + segments: [], + ttl: 10, + vastUrl: null, + vastXml: null, + width: 300, + }, + ], + }; + + it('should always return an array', function () { + let response = spec.interpretResponse(emptyResponseParam, bid); + expect(response).to.be.an('array'); + expect(response.length).equal(0); + response = spec.interpretResponse(fakeResponseParam, bid); + expect(response).to.be.an('array'); + expect(response.length).equal(1); + }); + }); +});