From 12351d82141dca61ffb1c931530e46708a80a218 Mon Sep 17 00:00:00 2001 From: dream-djaxtech Date: Tue, 13 Aug 2024 17:55:39 +0530 Subject: [PATCH 1/7] Djax bid adapter files added --- modules/djaxBidAdapter.js | 114 +++++++++++++++++++++++ modules/djaxBidAdapter.md | 51 ++++++++++ test/spec/modules/djaxBidAdapter_spec.js | 111 ++++++++++++++++++++++ 3 files changed, 276 insertions(+) create mode 100644 modules/djaxBidAdapter.js create mode 100644 modules/djaxBidAdapter.md create mode 100644 test/spec/modules/djaxBidAdapter_spec.js diff --git a/modules/djaxBidAdapter.js b/modules/djaxBidAdapter.js new file mode 100644 index 00000000000..a3cade4bcdb --- /dev/null +++ b/modules/djaxBidAdapter.js @@ -0,0 +1,114 @@ +import { registerBidder } from '../src/adapters/bidderFactory.js'; +import * as utils from '../src/utils.js'; +import {BANNER, VIDEO} from '../src/mediaTypes.js'; +import { ajax } from '../src/ajax.js'; +import {Renderer} from '../src/Renderer.js'; + +const SUPPORTED_AD_TYPES = [BANNER, VIDEO]; +const BIDDER_CODE = 'djax'; +const DOMAIN = 'https://revphpe.djaxbidder.com/header_bidding_vast/'; +const RENDERER_URL = 'https://acdn.adnxs.com/video/outstream/ANOutstreamVideo.js'; + +function outstreamRender(bidAd) { + bidAd.renderer.push(() => { + window.ANOutstreamVideo.renderAd({ + sizes: [bidAd.width, bidAd.height], + width: bidAd.width, + height: bidAd.height, + targetId: bidAd.adUnitCode, + adResponse: bidAd.adResponse, + rendererOptions: { + showVolume: false, + allowFullscreen: false + } + }); + }); +} + +function createRenderer(bidAd, rendererParams, adUnitCode) { + const renderer = Renderer.install({ + id: rendererParams.id, + url: rendererParams.url, + loaded: false, + config: {'player_height': bidAd.height, 'player_width': bidAd.width}, + adUnitCode + }); + try { + renderer.setRender(outstreamRender); + } catch (err) { + utils.logWarn('Prebid Error calling setRender on renderer', err); + } + return renderer; +} + +function sendResponseToServer(data) { + ajax(DOMAIN + 'www/admin/plugins/Prebid/tracking/track.php', null, JSON.stringify(data), { + withCredentials: false, + method: 'POST', + crossOrigin: true + }); +} + +export const spec = { + code: BIDDER_CODE, + supportedMediaTypes: SUPPORTED_AD_TYPES, + + isBidRequestValid: function(bid) { + return (typeof bid.params !== 'undefined' && parseInt(utils.getValue(bid.params, 'publisherId')) > 0); + }, + + buildRequests: function(validBidRequests) { + return { + method: 'POST', + url: DOMAIN + 'www/admin/plugins/Prebid/getAd.php', + options: { + withCredentials: false, + crossOrigin: true + }, + data: validBidRequests, + }; + }, + + interpretResponse: function(serverResponse, request) { + const response = serverResponse.body; + const bidResponses = []; + var bidRequestResponses = []; + + utils._each(response, function(bidAd) { + bidAd.adResponse = { + content: bidAd.vastXml, + height: bidAd.height, + width: bidAd.width + }; + + bidAd.renderer = bidAd.context === 'outstream' ? createRenderer(bidAd, { + id: bidAd.adUnitCode, + url: RENDERER_URL + }, bidAd.adUnitCode) : undefined; + bidResponses.push(bidAd); + }); + + bidRequestResponses.push({ + function: 'saveResponses', + request: request, + response: bidResponses + }); + sendResponseToServer(bidRequestResponses); + return bidResponses; + }, + + onBidWon: function(bid) { + let wonBids = []; + wonBids.push(bid); + wonBids[0].function = 'onBidWon'; + sendResponseToServer(wonBids); + }, + + onTimeout: function(details) { + details.unshift({ 'function': 'onTimeout' }); + sendResponseToServer(details); + } + +}; + +registerBidder(spec); diff --git a/modules/djaxBidAdapter.md b/modules/djaxBidAdapter.md new file mode 100644 index 00000000000..d36a92de458 --- /dev/null +++ b/modules/djaxBidAdapter.md @@ -0,0 +1,51 @@ +# Overview + +``` +Module Name: Djax Bidder Adapter +Module Type: Bidder Adapter +Maintainer: support@djaxtech.com +``` + +# Description + +Module that connects to Djax + +# Test Parameters +``` + var adUnits = [ + { + code: 'test-div', + mediaTypes: { + banner: { + sizes: [[300, 250]], // a display size + } + }, + bids: [ + { + bidder: "djax", + params: { + publisherId: '2' // string - required + } + } + ] + } + ]; +``` + var adUnits = [ + { + code: 'test-div', + mediaTypes: { + video: { + playerSize: [[480, 320]], // a display size + } + }, + bids: [ + { + bidder: "djax", + params: { + publisherId: '12' // string - required + } + } + ] + } + ]; \ No newline at end of file diff --git a/test/spec/modules/djaxBidAdapter_spec.js b/test/spec/modules/djaxBidAdapter_spec.js new file mode 100644 index 00000000000..f5025061253 --- /dev/null +++ b/test/spec/modules/djaxBidAdapter_spec.js @@ -0,0 +1,111 @@ +import { expect } from 'chai'; +import { newBidder } from 'src/adapters/bidderFactory.js'; +import { spec } from '../../../modules/djaxBidAdapter.js'; + +const DOMAIN = 'https://revphpe.djaxbidder.com/header_bidding_vast/'; +const ENDPOINT = DOMAIN + 'www/admin/plugins/Prebid/getAd.php'; + +describe('Djax Adapter', function() { + const adapter = newBidder(spec); + + describe('inherited functions', function () { + it('should exists and is a function', function () { + expect(adapter.callBids).to.exist.and.to.be.a('function'); + }); + }); + + describe('isBidRequestValidForBanner', () => { + let bid = { + 'bidder': 'djax', + 'params': { + 'publisherId': 2 + }, + 'adUnitCode': 'adunit-code', + 'mediaTypes': { + 'banner': { + 'sizes': [ + [300, 250] + ] + } + }, + 'sizes': [[300, 250]], + 'bidId': '26e88c3c703e66', + 'bidderRequestId': '1a8ff729f6c1a3', + 'auctionId': 'cb65d954-ffe1-4f4a-8603-02b521c00333', + }; + + it('should return true when required params found', () => { + expect(spec.isBidRequestValid(bid)).to.equal(true); + }); + + it('should return false when required params are not passed', () => { + let bid = Object.assign({}, bid); + delete bid.params; + bid.params = { + wrong: 'missing publisherId' + }; + expect(spec.isBidRequestValid(bid)).to.equal(false); + }); + }); + + describe('buildRequestsForBanner', () => { + let bidRequests = [ + { + 'bidder': 'djax', + 'params': { + 'publisherId': 2 + }, + 'adUnitCode': 'adunit-code', + 'mediaTypes': { + 'banner': { + 'sizes': [ + [300, 250] + ] + } + }, + 'sizes': [[300, 250]], + 'bidId': '26e88c3c703e66', + 'bidderRequestId': '1a8ff729f6c1a3', + 'auctionId': 'cb65d954-ffe1-4f4a-8603-02b521c00333' + } + ]; + + it('sends bid request to ENDPOINT via POST', () => { + const request = spec.buildRequests(bidRequests); + expect(request.url).to.contain(ENDPOINT); + expect(request.method).to.equal('POST'); + }); + }); + + describe('interpretResponseForBanner', () => { + let bidRequests = [ + { + 'bidder': 'djax', + 'params': { + 'publisherId': 2 + }, + 'adUnitCode': 'adunit-code', + 'mediaTypes': { + 'banner': { + 'sizes': [ + [300, 250] + ] + } + }, + 'sizes': [[300, 250]], + 'bidId': '26e88c3c703e66', + 'bidderRequestId': '1a8ff729f6c1a3', + 'auctionId': 'cb65d954-ffe1-4f4a-8603-02b521c00333' + } + ]; + + it('handles nobid responses', () => { + var request = spec.buildRequests(bidRequests); + let response = ''; + + let result = spec.interpretResponse(response, request[0]); + expect(result.length).to.equal(0); + }); + }); + +}); From 036f8766f012458a8a6bdd582d2c95789e7a0fe7 Mon Sep 17 00:00:00 2001 From: Chris Huie Date: Tue, 13 Aug 2024 12:15:55 -0600 Subject: [PATCH 2/7] fix linting issue From 082138f7be66ad00727119d62b528b5cfa5231d0 Mon Sep 17 00:00:00 2001 From: dream-djaxtech Date: Wed, 14 Aug 2024 18:34:39 +0530 Subject: [PATCH 3/7] Linting issue fixed --- test/spec/modules/djaxBidAdapter_spec.js | 2 -- 1 file changed, 2 deletions(-) diff --git a/test/spec/modules/djaxBidAdapter_spec.js b/test/spec/modules/djaxBidAdapter_spec.js index f5025061253..0f3615b8f46 100644 --- a/test/spec/modules/djaxBidAdapter_spec.js +++ b/test/spec/modules/djaxBidAdapter_spec.js @@ -102,10 +102,8 @@ describe('Djax Adapter', function() { it('handles nobid responses', () => { var request = spec.buildRequests(bidRequests); let response = ''; - let result = spec.interpretResponse(response, request[0]); expect(result.length).to.equal(0); }); }); - }); From 165b0d4150a5cce77d33a13522bb4583752a639b Mon Sep 17 00:00:00 2001 From: Patrick McCann Date: Wed, 14 Aug 2024 15:24:18 -0400 Subject: [PATCH 4/7] Update djaxBidAdapter.js --- modules/djaxBidAdapter.js | 1 - 1 file changed, 1 deletion(-) diff --git a/modules/djaxBidAdapter.js b/modules/djaxBidAdapter.js index a3cade4bcdb..7a9e359f520 100644 --- a/modules/djaxBidAdapter.js +++ b/modules/djaxBidAdapter.js @@ -108,7 +108,6 @@ export const spec = { details.unshift({ 'function': 'onTimeout' }); sendResponseToServer(details); } - }; registerBidder(spec); From 04295841dd6f1706a731c75511f19d4f7c3dc665 Mon Sep 17 00:00:00 2001 From: dream-djaxtech <62751516+dream-djaxtech@users.noreply.github.com> Date: Fri, 16 Aug 2024 11:06:34 +0530 Subject: [PATCH 5/7] Update djaxBidAdapter_spec.js --- test/spec/modules/djaxBidAdapter_spec.js | 5 ----- 1 file changed, 5 deletions(-) diff --git a/test/spec/modules/djaxBidAdapter_spec.js b/test/spec/modules/djaxBidAdapter_spec.js index 0f3615b8f46..75c34aab8d3 100644 --- a/test/spec/modules/djaxBidAdapter_spec.js +++ b/test/spec/modules/djaxBidAdapter_spec.js @@ -39,11 +39,6 @@ describe('Djax Adapter', function() { }); it('should return false when required params are not passed', () => { - let bid = Object.assign({}, bid); - delete bid.params; - bid.params = { - wrong: 'missing publisherId' - }; expect(spec.isBidRequestValid(bid)).to.equal(false); }); }); From 33f6d234e3633bc5c95c8d55807f90a0130bc5f3 Mon Sep 17 00:00:00 2001 From: dream-djaxtech <62751516+dream-djaxtech@users.noreply.github.com> Date: Fri, 16 Aug 2024 11:15:39 +0530 Subject: [PATCH 6/7] Update djaxBidAdapter_spec.js --- test/spec/modules/djaxBidAdapter_spec.js | 4 ---- 1 file changed, 4 deletions(-) diff --git a/test/spec/modules/djaxBidAdapter_spec.js b/test/spec/modules/djaxBidAdapter_spec.js index 75c34aab8d3..afa9a36eab7 100644 --- a/test/spec/modules/djaxBidAdapter_spec.js +++ b/test/spec/modules/djaxBidAdapter_spec.js @@ -37,10 +37,6 @@ describe('Djax Adapter', function() { it('should return true when required params found', () => { expect(spec.isBidRequestValid(bid)).to.equal(true); }); - - it('should return false when required params are not passed', () => { - expect(spec.isBidRequestValid(bid)).to.equal(false); - }); }); describe('buildRequestsForBanner', () => { From 96675a6771dfbc8f30e597b92bf2ac279e9da123 Mon Sep 17 00:00:00 2001 From: dream-djaxtech Date: Wed, 28 Aug 2024 11:13:05 +0530 Subject: [PATCH 7/7] Djax Analytic Adapter submission --- modules/djaxAnalyticsAdapter.js | 152 ++++++++++++++++++++++++++++++++ modules/djaxAnalyticsAdapter.md | 48 ++++++++++ 2 files changed, 200 insertions(+) create mode 100644 modules/djaxAnalyticsAdapter.js create mode 100644 modules/djaxAnalyticsAdapter.md diff --git a/modules/djaxAnalyticsAdapter.js b/modules/djaxAnalyticsAdapter.js new file mode 100644 index 00000000000..3f93b6a3863 --- /dev/null +++ b/modules/djaxAnalyticsAdapter.js @@ -0,0 +1,152 @@ +import AnalyticsAdapter from '../libraries/analyticsAdapter/AnalyticsAdapter.js'; +import {prefixLog, isPlainObject} from '../src/utils.js'; +import {has as hasEvent} from '../src/events.js'; +import adapterManager from '../src/adapterManager.js'; +import {ajaxBuilder} from '../src/ajax.js'; + +const DEFAULTS = { + batchSize: 1, + batchDelay: 100, + method: 'POST' +} + +const TYPES = { + handler: 'function', + batchSize: 'number', + batchDelay: 'number', + gvlid: 'number', +} + +const MAX_CALL_DEPTH = 20; + +export function DjaxAnalytics() { + const parent = AnalyticsAdapter({analyticsType: 'endpoint'}); + const {logError, logWarn} = prefixLog('Djax analytics:'); + let batch = []; + let callDepth = 0; + let options, handler, timer, translate; + + function optionsAreValid(options) { + if (!options.url && !options.handler) { + logError('options must specify either `url` or `handler`') + return false; + } + if (options.hasOwnProperty('method') && !['GET', 'POST'].includes(options.method)) { + logError('options.method must be GET or POST'); + return false; + } + for (const [field, type] of Object.entries(TYPES)) { + // eslint-disable-next-line valid-typeof + if (options.hasOwnProperty(field) && typeof options[field] !== type) { + logError(`options.${field} must be a ${type}`); + return false; + } + } + if (options.hasOwnProperty('events')) { + if (!isPlainObject(options.events)) { + logError('options.events must be an object'); + return false; + } + for (const [event, handler] of Object.entries(options.events)) { + if (!hasEvent(event)) { + logWarn(`options.events.${event} does not match any known Prebid event`); + } + if (typeof handler !== 'function') { + logError(`options.events.${event} must be a function`); + return false; + } + } + } + return true; + } + + function processBatch() { + const currentBatch = batch; + batch = []; + callDepth++; + try { + // the pub-provided handler may inadvertently cause an infinite chain of events; + // even just logging an exception from it may cause an AUCTION_DEBUG event, that + // gets back to the handler, that throws another exception etc. + // to avoid the issue, put a cap on recursion + if (callDepth === MAX_CALL_DEPTH) { + logError('detected probable infinite recursion, discarding events', currentBatch); + } + if (callDepth >= MAX_CALL_DEPTH) { + return; + } + try { + handler(currentBatch); + } catch (e) { + logError('error executing options.handler', e); + } + } finally { + callDepth--; + } + } + + function translator(eventHandlers) { + if (!eventHandlers) { + return (data) => data; + } + return function ({eventType, args}) { + if (eventHandlers.hasOwnProperty(eventType)) { + try { + return eventHandlers[eventType](args); + } catch (e) { + logError(`error executing options.events.${eventType}`, e); + } + } + } + } + + return Object.assign( + Object.create(parent), + { + gvlid(config) { + return config?.options?.gvlid + }, + enableAnalytics(config) { + if (optionsAreValid(config?.options || {})) { + options = Object.assign({}, DEFAULTS, config.options); + handler = options.handler || defaultHandler(options); + translate = translator(options.events); + parent.enableAnalytics.call(this, config); + } + }, + track(event) { + const datum = translate(event); + if (datum != null) { + batch.push(datum); + if (timer != null) { + clearTimeout(timer); + timer = null; + } + if (batch.length >= options.batchSize) { + processBatch(); + } else { + timer = setTimeout(processBatch, options.batchDelay); + } + } + } + } + ) +} + +export function defaultHandler({url, method, batchSize, ajax = ajaxBuilder()}) { + const callbacks = { + success() {}, + error() {} + } + const extract = batchSize > 1 ? (events) => events : (events) => events[0]; + const serialize = method === 'GET' ? (data) => ({data: JSON.stringify(data)}) : (data) => JSON.stringify(data); + + return function (events) { + ajax(url, callbacks, serialize(extract(events)), {method, keepalive: true}) + } +} + +adapterManager.registerAnalyticsAdapter({ + adapter: DjaxAnalytics(), + code: 'djax', +}); diff --git a/modules/djaxAnalyticsAdapter.md b/modules/djaxAnalyticsAdapter.md new file mode 100644 index 00000000000..70ed8c1d704 --- /dev/null +++ b/modules/djaxAnalyticsAdapter.md @@ -0,0 +1,48 @@ +# Overview +``` +Module Name: djax Analytics Adapter +Module Type: Analytics Adapter +Maintainer: support@djaxtech.com +``` + +### Usage + +The djax analytics adapter can be used by all clients . + +### Example Configuration + +```javascript +pbjs.enableAnalytics({ + provider: 'djax', + options: { + options: { + url: 'https://example.com', // change your end point url to fetch the tracked information + } +}); + + +// Based on events +pbjs.enableAnalytics({ + provider: 'djax', + options: { + url: 'https://example.com', + batchSize: 10, + events: { + bidRequested(request) { + return { + type: 'REQUEST', + auctionId: request.auctionId, + bidder: request.bidderCode + } + }, + bidResponse(response) { + return { + type: 'RESPONSE', + auctionId: response.auctionId, + bidder: response.bidderCode + } + } + } + } +}) +```