diff --git a/modules/concertAnalyticsAdapter.js b/modules/concertAnalyticsAdapter.js deleted file mode 100644 index a81d07e63b55..000000000000 --- a/modules/concertAnalyticsAdapter.js +++ /dev/null @@ -1,120 +0,0 @@ -import {ajax} from '../src/ajax.js'; -import adapter from '../src/AnalyticsAdapter.js'; -import CONSTANTS from '../src/constants.json'; -import adapterManager from '../src/adapterManager.js'; -import * as utils from '../src/utils.js'; - -const analyticsType = 'endpoint'; - -// We only want to send about 1% of events for sampling purposes -const SAMPLE_RATE_PERCENTAGE = 1 / 100; -const pageIncludedInSample = sampleAnalytics(); - -const url = 'https://bids.concert.io/analytics'; - -const { - EVENTS: { - BID_RESPONSE, - BID_WON, - AUCTION_END - } -} = CONSTANTS; - -let queue = []; - -let concertAnalytics = Object.assign(adapter({url, analyticsType}), { - track({ eventType, args }) { - switch (eventType) { - case BID_RESPONSE: - if (args.bidder !== 'concert') break; - queue.push(mapBidEvent(eventType, args)); - break; - - case BID_WON: - if (args.bidder !== 'concert') break; - queue.push(mapBidEvent(eventType, args)); - break; - - case AUCTION_END: - // Set a delay, as BID_WON events will come after AUCTION_END events - setTimeout(() => sendEvents(), 3000); - break; - - default: - break; - } - } -}); - -function mapBidEvent(eventType, args) { - const { adId, auctionId, cpm, creativeId, width, height, timeToRespond } = args; - const [gamCreativeId, concertRequestId] = getConcertRequestId(creativeId); - - const payload = { - event: eventType, - concert_rid: concertRequestId, - adId, - auctionId, - creativeId: gamCreativeId, - position: args.adUnitCode, - url: window.location.href, - cpm, - width, - height, - timeToRespond - } - - return payload; -} - -/** - * In order to pass back the concert_rid from CBS, it is tucked into the `creativeId` - * slot in the bid response and combined with a pipe `|`. This method splits the creative ID - * and the concert_rid. - * - * @param {string} creativeId - */ -function getConcertRequestId(creativeId) { - if (!creativeId || creativeId.indexOf('|') < 0) return [null, null]; - - return creativeId.split('|'); -} - -function sampleAnalytics() { - return Math.random() <= SAMPLE_RATE_PERCENTAGE; -} - -function sendEvents() { - concertAnalytics.eventsStorage = queue; - - if (!queue.length) return; - - if (!pageIncludedInSample) { - utils.logMessage('Page not included in sample for Concert Analytics'); - return; - } - - try { - const body = JSON.stringify(queue); - ajax(url, () => queue = [], body, { - contentType: 'application/json', - method: 'POST' - }); - } catch (err) { utils.logMessage('Concert Analytics error') } -} - -// save the base class function -concertAnalytics.originEnableAnalytics = concertAnalytics.enableAnalytics; -concertAnalytics.eventsStorage = []; - -// override enableAnalytics so we can get access to the config passed in from the page -concertAnalytics.enableAnalytics = function (config) { - concertAnalytics.originEnableAnalytics(config); -}; - -adapterManager.registerAnalyticsAdapter({ - adapter: concertAnalytics, - code: 'concert' -}); - -export default concertAnalytics; diff --git a/modules/concertAnalyticsAdapter.md b/modules/concertAnalyticsAdapter.md deleted file mode 100644 index 7c9b6d227032..000000000000 --- a/modules/concertAnalyticsAdapter.md +++ /dev/null @@ -1,11 +0,0 @@ -# Overview - -``` -Module Name: Concert Analytics Adapter -Module Type: Analytics Adapter -Maintainer: support@concert.io -``` - -# Description - -Analytics adapter for concert. \ No newline at end of file diff --git a/modules/concertBidAdapter.js b/modules/concertBidAdapter.js deleted file mode 100644 index d153ddf9ee24..000000000000 --- a/modules/concertBidAdapter.js +++ /dev/null @@ -1,208 +0,0 @@ - -import * as utils from '../src/utils.js'; -import { registerBidder } from '../src/adapters/bidderFactory.js'; -import { getStorageManager } from '../src/storageManager.js' - -const BIDDER_CODE = 'concert'; -const CONCERT_ENDPOINT = 'https://bids.concert.io'; -const USER_SYNC_URL = 'https://cdn.concert.io/lib/bids/sync.html'; - -export const spec = { - code: BIDDER_CODE, - /** - * Determines whether or not the given bid request is valid. - * - * @param {BidRequest} bid The bid params to validate. - * @return boolean True if this is a valid bid, and false otherwise. - */ - isBidRequestValid: function(bid) { - if (!bid.params.partnerId) { - utils.logWarn('Missing partnerId bid parameter'); - return false; - } - - return true; - }, - - /** - * Make a server request from the list of BidRequests. - * - * @param {validBidRequests[]} - an array of bids - * @param {bidderRequest} - - * @return ServerRequest Info describing the request to the server. - */ - buildRequests: function(validBidRequests, bidderRequest) { - utils.logMessage(validBidRequests); - utils.logMessage(bidderRequest); - let payload = { - meta: { - prebidVersion: '$prebid.version$', - pageUrl: bidderRequest.refererInfo.referer, - screen: [window.screen.width, window.screen.height].join('x'), - debug: utils.debugTurnedOn(), - uid: getUid(bidderRequest), - optedOut: hasOptedOutOfPersonalization(), - adapterVersion: '1.1.0', - uspConsent: bidderRequest.uspConsent, - gdprConsent: bidderRequest.gdprConsent - } - } - - payload.slots = validBidRequests.map(bidRequest => { - let slot = { - name: bidRequest.adUnitCode, - bidId: bidRequest.bidId, - transactionId: bidRequest.transactionId, - sizes: bidRequest.sizes, - partnerId: bidRequest.params.partnerId, - slotType: bidRequest.params.slotType - } - - return slot; - }); - - utils.logMessage(payload); - - return { - method: 'POST', - url: `${CONCERT_ENDPOINT}/bids/prebid`, - data: JSON.stringify(payload) - } - }, - /** - * Unpack the response from the server into a list of bids. - * - * @param {ServerResponse} serverResponse A successful response from the server. - * @return {Bid[]} An array of bids which were nested inside the server. - */ - interpretResponse: function(serverResponse, bidRequest) { - utils.logMessage(serverResponse); - utils.logMessage(bidRequest); - - const serverBody = serverResponse.body; - - if (!serverBody || typeof serverBody !== 'object') { - return []; - } - - let bidResponses = []; - - bidResponses = serverBody.bids.map(bid => { - return { - requestId: bid.bidId, - cpm: bid.cpm, - width: bid.width, - height: bid.height, - ad: bid.ad, - ttl: bid.ttl, - creativeId: bid.creativeId, - netRevenue: bid.netRevenue, - currency: bid.currency - } - }); - - if (utils.debugTurnedOn() && serverBody.debug) { - utils.logMessage(`CONCERT`, serverBody.debug); - } - - utils.logMessage(bidResponses); - return bidResponses; - }, - - /** - * Register the user sync pixels which should be dropped after the auction. - * - * @param {SyncOptions} syncOptions Which user syncs are allowed? - * @param {ServerResponse[]} serverResponses List of server's responses. - * @param {gdprConsent} object GDPR consent object. - * @param {uspConsent} string US Privacy String. - * @return {UserSync[]} The user syncs which should be dropped. - */ - getUserSyncs: function(syncOptions, serverResponses, gdprConsent, uspConsent) { - const syncs = [] - if (syncOptions.iframeEnabled && !hasOptedOutOfPersonalization()) { - let params = []; - - if (gdprConsent && (typeof gdprConsent.gdprApplies === 'boolean')) { - params.push(`gdpr_applies=${gdprConsent.gdprApplies ? '1' : '0'}`); - } - if (gdprConsent && (typeof gdprConsent.consentString === 'string')) { - params.push(`gdpr_consent=${gdprConsent.consentString}`); - } - if (uspConsent && (typeof uspConsent === 'string')) { - params.push(`usp_consent=${uspConsent}`); - } - - syncs.push({ - type: 'iframe', - url: USER_SYNC_URL + (params.length > 0 ? `?${params.join('&')}` : '') - }); - } - return syncs; - }, - - /** - * Register bidder specific code, which will execute if bidder timed out after an auction - * @param {data} Containing timeout specific data - */ - onTimeout: function(data) { - utils.logMessage('concert bidder timed out'); - utils.logMessage(data); - }, - - /** - * Register bidder specific code, which will execute if a bid from this bidder won the auction - * @param {Bid} The bid that won the auction - */ - onBidWon: function(bid) { - utils.logMessage('concert bidder won bid'); - utils.logMessage(bid); - } - -} - -registerBidder(spec); - -const storage = getStorageManager(); - -/** - * Check or generate a UID for the current user. - */ -function getUid(bidderRequest) { - if (hasOptedOutOfPersonalization() || !consentAllowsPpid(bidderRequest)) { - return false; - } - - const CONCERT_UID_KEY = 'c_uid'; - - let uid = storage.getDataFromLocalStorage(CONCERT_UID_KEY); - - if (!uid) { - uid = utils.generateUUID(); - storage.setDataInLocalStorage(CONCERT_UID_KEY, uid); - } - - return uid; -} - -/** - * Whether the user has opted out of personalization. - */ -function hasOptedOutOfPersonalization() { - const CONCERT_NO_PERSONALIZATION_KEY = 'c_nap'; - - return storage.getDataFromLocalStorage(CONCERT_NO_PERSONALIZATION_KEY) === 'true'; -} - -/** - * Whether the privacy consent strings allow personalization. - * - * @param {BidderRequest} bidderRequest Object which contains any data consent signals - */ -function consentAllowsPpid(bidderRequest) { - /* NOTE: We cannot easily test GDPR consent, without the - * `consent-string` npm module; so will have to rely on that - * happening on the bid-server. */ - return !(bidderRequest.uspConsent === 'string' && - bidderRequest.uspConsent.toUpperCase().substring(0, 2) === '1YY') -} diff --git a/modules/concertBidAdapter.md b/modules/concertBidAdapter.md deleted file mode 100644 index faf774946d1e..000000000000 --- a/modules/concertBidAdapter.md +++ /dev/null @@ -1,33 +0,0 @@ -# Overview - -``` -Module Name: Concert Bid Adapter -Module Type: Bidder Adapter -Maintainer: support@concert.io -``` - -# Description - -Module that connects to Concert demand sources - -# Test Paramters -``` - var adUnits = [ - { - code: 'desktop_leaderboard_variable', - mediaTypes: { - banner: { - sizes: [[1030, 590]] - } - } - bids: [ - { - bidder: "concert", - params: { - partnerId: 'test_partner' - } - } - ] - } - ]; -``` \ No newline at end of file diff --git a/test/spec/modules/concertAnalyticsAdapter_spec.js b/test/spec/modules/concertAnalyticsAdapter_spec.js deleted file mode 100644 index b0aad2f31565..000000000000 --- a/test/spec/modules/concertAnalyticsAdapter_spec.js +++ /dev/null @@ -1,157 +0,0 @@ -import concertAnalytics from 'modules/concertAnalyticsAdapter.js'; -import { expect } from 'chai'; -const sinon = require('sinon'); -let adapterManager = require('src/adapterManager').default; -let events = require('src/events'); -let constants = require('src/constants.json'); - -describe('ConcertAnalyticsAdapter', function() { - let sandbox; - let xhr; - let requests; - let clock; - let timestamp = 1896134400; - let auctionId = '9f894496-10fe-4652-863d-623462bf82b8'; - let timeout = 1000; - - before(function () { - sandbox = sinon.createSandbox(); - xhr = sandbox.useFakeXMLHttpRequest(); - requests = []; - - xhr.onCreate = function (request) { - requests.push(request); - }; - clock = sandbox.useFakeTimers(1896134400); - }); - - after(function () { - sandbox.restore(); - }); - - describe('track', function() { - beforeEach(function () { - sandbox.stub(events, 'getEvents').returns([]); - - adapterManager.enableAnalytics({ - provider: 'concert' - }); - }); - - afterEach(function () { - events.getEvents.restore(); - concertAnalytics.eventsStorage = []; - concertAnalytics.disableAnalytics(); - }); - - it('should catch all events', function() { - sandbox.spy(concertAnalytics, 'track'); - - fireBidEvents(events); - sandbox.assert.callCount(concertAnalytics.track, 5); - }); - - it('should report data for BID_RESPONSE, BID_WON events', function() { - fireBidEvents(events); - clock.tick(3000 + 1000); - - const eventsToReport = ['bidResponse', 'bidWon']; - for (var i = 0; i < concertAnalytics.eventsStorage.length; i++) { - expect(eventsToReport.indexOf(concertAnalytics.eventsStorage[i].event)).to.be.above(-1); - } - - for (var i = 0; i < eventsToReport.length; i++) { - expect(concertAnalytics.eventsStorage.some(function(event) { - return event.event === eventsToReport[i] - })).to.equal(true); - } - }); - - it('should report data in the shape expected by analytics endpoint', function() { - fireBidEvents(events); - clock.tick(3000 + 1000); - - const requiredFields = ['event', 'concert_rid', 'adId', 'auctionId', 'creativeId', 'position', 'url', 'cpm', 'width', 'height', 'timeToRespond']; - - for (var i = 0; i < requiredFields.length; i++) { - expect(concertAnalytics.eventsStorage[0]).to.have.property(requiredFields[i]); - } - }); - }); - - const adUnits = [{ - code: 'desktop_leaderboard_variable', - sizes: [[1030, 590]], - mediaTypes: { - banner: { - sizes: [[1030, 590]] - } - }, - bids: [ - { - bidder: 'concert', - params: { - partnerId: 'test_partner' - } - } - ] - }]; - - const bidResponse = { - 'bidderCode': 'concert', - 'width': 1030, - 'height': 590, - 'statusMessage': 'Bid available', - 'adId': '642f13fe18ab7dc', - 'requestId': '4062fba2e039919', - 'mediaType': 'banner', - 'source': 'client', - 'cpm': 6, - 'ad': '', - 'ttl': 360, - 'creativeId': '138308483085|62bac030-a5d3-11ea-b3be-55590c8153a5', - 'netRevenue': false, - 'currency': 'USD', - 'originalCpm': 6, - 'originalCurrency': 'USD', - 'auctionId': '9f894496-10fe-4652-863d-623462bf82b8', - 'responseTimestamp': 1591213790366, - 'requestTimestamp': 1591213790017, - 'bidder': 'concert', - 'adUnitCode': 'desktop_leaderboard_variable', - 'timeToRespond': 349, - 'status': 'rendered', - 'params': [ - { - 'partnerId': 'cst' - } - ] - } - - const bidWon = { - 'adId': '642f13fe18ab7dc', - 'mediaType': 'banner', - 'requestId': '4062fba2e039919', - 'cpm': 6, - 'creativeId': '138308483085|62bac030-a5d3-11ea-b3be-55590c8153a5', - 'currency': 'USD', - 'netRevenue': false, - 'ttl': 360, - 'auctionId': '9f894496-10fe-4652-863d-623462bf82b8', - 'statusMessage': 'Bid available', - 'responseTimestamp': 1591213790366, - 'requestTimestamp': 1591213790017, - 'bidder': 'concert', - 'adUnitCode': 'desktop_leaderboard_variable', - 'sizes': [[1030, 590]], - 'size': [1030, 590] - } - - function fireBidEvents(events) { - events.emit(constants.EVENTS.AUCTION_INIT, {timestamp, auctionId, timeout, adUnits}); - events.emit(constants.EVENTS.BID_REQUESTED, {bidder: 'concert'}); - events.emit(constants.EVENTS.BID_RESPONSE, bidResponse); - events.emit(constants.EVENTS.AUCTION_END, {}); - events.emit(constants.EVENTS.BID_WON, bidWon); - } -}); diff --git a/test/spec/modules/concertBidAdapter_spec.js b/test/spec/modules/concertBidAdapter_spec.js deleted file mode 100644 index df999f45df9c..000000000000 --- a/test/spec/modules/concertBidAdapter_spec.js +++ /dev/null @@ -1,219 +0,0 @@ -import { expect } from 'chai'; -import sinon from 'sinon'; -import { spec } from 'modules/concertBidAdapter.js'; -import { getStorageManager } from '../../../src/storageManager.js' - -describe('ConcertAdapter', function () { - let bidRequests; - let bidRequest; - let bidResponse; - - beforeEach(function () { - bidRequests = [ - { - bidder: 'concert', - params: { - partnerId: 'foo', - slotType: 'fizz' - }, - adUnitCode: 'desktop_leaderboard_variable', - bidId: 'foo', - transactionId: '', - sizes: [[1030, 590]] - } - ]; - - bidRequest = { - refererInfo: { - referer: 'https://www.google.com' - }, - uspConsent: '1YYY', - gdprConsent: {} - }; - - bidResponse = { - body: { - bids: [ - { - bidId: '16d2e73faea32d9', - cpm: '6', - width: '1030', - height: '590', - ad: '', - ttl: '360', - creativeId: '123349|a7d62700-a4bf-11ea-829f-ad3b0b7a9383', - netRevenue: false, - currency: 'USD' - } - ] - } - } - }); - - describe('spec.isBidRequestValid', function() { - it('should return when it recieved all the required params', function() { - const bid = bidRequests[0]; - expect(spec.isBidRequestValid(bid)).to.equal(true); - }); - - it('should return false when partner id is missing', function() { - const bid = { - bidder: 'concert', - params: {} - } - - expect(spec.isBidRequestValid(bid)).to.equal(false); - }); - }); - - describe('spec.buildRequests', function() { - it('should build a payload object with the shape expected by server', function() { - const request = spec.buildRequests(bidRequests, bidRequest); - const payload = JSON.parse(request.data); - expect(payload).to.have.property('meta'); - expect(payload).to.have.property('slots'); - - const metaRequiredFields = ['prebidVersion', 'pageUrl', 'screen', 'debug', 'uid', 'optedOut', 'adapterVersion', 'uspConsent', 'gdprConsent']; - const slotsRequiredFields = ['name', 'bidId', 'transactionId', 'sizes', 'partnerId', 'slotType']; - - metaRequiredFields.forEach(function(field) { - expect(payload.meta).to.have.property(field); - }); - slotsRequiredFields.forEach(function(field) { - expect(payload.slots[0]).to.have.property(field); - }); - }); - - it('should not generate uid if the user has opted out', function() { - const storage = getStorageManager(); - storage.setDataInLocalStorage('c_nap', 'true'); - const request = spec.buildRequests(bidRequests, bidRequest); - const payload = JSON.parse(request.data); - - expect(payload.meta.uid).to.equal(false); - }); - - it('should generate uid if the user has not opted out', function() { - const storage = getStorageManager(); - storage.removeDataFromLocalStorage('c_nap'); - const request = spec.buildRequests(bidRequests, bidRequest); - const payload = JSON.parse(request.data); - - expect(payload.meta.uid).to.not.equal(false); - }); - - it('should grab uid from local storage if it exists', function() { - const storage = getStorageManager(); - storage.setDataInLocalStorage('c_uid', 'foo'); - storage.removeDataFromLocalStorage('c_nap'); - const request = spec.buildRequests(bidRequests, bidRequest); - const payload = JSON.parse(request.data); - - expect(payload.meta.uid).to.equal('foo'); - }); - }); - - describe('spec.interpretResponse', function() { - it('should return bids in the shape expected by prebid', function() { - const bids = spec.interpretResponse(bidResponse, bidRequest); - const requiredFields = ['requestId', 'cpm', 'width', 'height', 'ad', 'ttl', 'creativeId', 'netRevenue', 'currency']; - - requiredFields.forEach(function(field) { - expect(bids[0]).to.have.property(field); - }); - }); - - it('should return empty bids if there is no response from server', function() { - const bids = spec.interpretResponse({ body: null }, bidRequest); - expect(bids).to.have.lengthOf(0); - }); - - it('should return empty bids if there are no bids from the server', function() { - const bids = spec.interpretResponse({ body: {bids: []} }, bidRequest); - expect(bids).to.have.lengthOf(0); - }); - }); - - describe('spec.getUserSyncs', function() { - it('should not register syncs when iframe is not enabled', function() { - const opts = { - iframeEnabled: false - } - const sync = spec.getUserSyncs(opts, [], bidRequest.gdprConsent, bidRequest.uspConsent); - expect(sync).to.have.lengthOf(0); - }); - - it('should not register syncs when the user has opted out', function() { - const opts = { - iframeEnabled: true - }; - const storage = getStorageManager(); - storage.setDataInLocalStorage('c_nap', 'true'); - - const sync = spec.getUserSyncs(opts, [], bidRequest.gdprConsent, bidRequest.uspConsent); - expect(sync).to.have.lengthOf(0); - }); - - it('should set gdprApplies flag to 1 if the user is in area where GDPR applies', function() { - const opts = { - iframeEnabled: true - }; - const storage = getStorageManager(); - storage.removeDataFromLocalStorage('c_nap'); - - bidRequest.gdprConsent = { - gdprApplies: true - }; - - const sync = spec.getUserSyncs(opts, [], bidRequest.gdprConsent, bidRequest.uspConsent); - expect(sync[0].url).to.have.string('gdpr_applies=1'); - }); - - it('should set gdprApplies flag to 1 if the user is in area where GDPR applies', function() { - const opts = { - iframeEnabled: true - }; - const storage = getStorageManager(); - storage.removeDataFromLocalStorage('c_nap'); - - bidRequest.gdprConsent = { - gdprApplies: false - }; - - const sync = spec.getUserSyncs(opts, [], bidRequest.gdprConsent, bidRequest.uspConsent); - expect(sync[0].url).to.have.string('gdpr_applies=0'); - }); - - it('should set gdpr consent param with the user\'s choices on consent', function() { - const opts = { - iframeEnabled: true - }; - const storage = getStorageManager(); - storage.removeDataFromLocalStorage('c_nap'); - - bidRequest.gdprConsent = { - gdprApplies: false, - consentString: 'BOJ/P2HOJ/P2HABABMAAAAAZ+A==' - }; - - const sync = spec.getUserSyncs(opts, [], bidRequest.gdprConsent, bidRequest.uspConsent); - expect(sync[0].url).to.have.string('gdpr_consent=BOJ/P2HOJ/P2HABABMAAAAAZ+A=='); - }); - - it('should set ccpa consent param with the user\'s choices on consent', function() { - const opts = { - iframeEnabled: true - }; - const storage = getStorageManager(); - storage.removeDataFromLocalStorage('c_nap'); - - bidRequest.gdprConsent = { - gdprApplies: false, - uspConsent: '1YYY' - }; - - const sync = spec.getUserSyncs(opts, [], bidRequest.gdprConsent, bidRequest.uspConsent); - expect(sync[0].url).to.have.string('usp_consent=1YY'); - }); - }); -});