From 4f06edcbd3afaee42c1c1cfde30f4192e47a59c9 Mon Sep 17 00:00:00 2001 From: Ziv Bruchian Date: Wed, 20 Jun 2018 14:29:28 +0300 Subject: [PATCH 1/7] My6sense new adapter --- modules/my6senseBidAdapter.js | 202 +++++++++++++++++++ modules/my6senseBidAdapter.md | 35 ++++ test/spec/modules/my6senseBidAdapter_spec.js | 151 ++++++++++++++ 3 files changed, 388 insertions(+) create mode 100644 modules/my6senseBidAdapter.js create mode 100644 modules/my6senseBidAdapter.md create mode 100644 test/spec/modules/my6senseBidAdapter_spec.js diff --git a/modules/my6senseBidAdapter.js b/modules/my6senseBidAdapter.js new file mode 100644 index 00000000000..b6d12777efe --- /dev/null +++ b/modules/my6senseBidAdapter.js @@ -0,0 +1,202 @@ +require('babel-core/register')({ + ignore: /node_modules\/(?!ProjectB)/ +}); +const {registerBidder} = require('../src/adapters/bidderFactory'); +const BIDDER_CODE = 'my6sense'; +// production +// const END_POINT = 'http://papi.mynativeplatform.com/pub2/web/hbwidget.json'; +// local +const END_POINT = 'http://127.0.0.1:8080/pub2/web/hbwidget.json'; +// Qa +// const END_POINT = 'http://54.237.134.160:8080/pub2/web/hbwidget.json'; +const END_POINT_METHOD = 'POST'; + +// called first +function isBidRequestValid(bid) { + return !(bid.bidder !== BIDDER_CODE || !bid.params || !bid.params.key); +} + +function getUrl(url) { + if (!url) { + url = window.location.href;// "clean" url of current web page + } + var canonicalLink = null; + // first look for meta data with property "og:url" + var metaElements = document.getElementsByTagName('meta'); + for (var i = 0; i < metaElements.length && !canonicalLink; i++) { + if (metaElements[i].getAttribute('property') == 'og:url') { + canonicalLink = metaElements[i].content; + } + } + if (!canonicalLink) { + var canonicalLinkContainer = document.querySelector("link[rel='canonical']");// html element containing the canonical link + if (canonicalLinkContainer) { + // get clean url from href of + canonicalLink = canonicalLinkContainer.href; + } + } + url = canonicalLink || url; + return encodeURIComponent(url).toString(); +} + +/** + * this function is used to fix param value before sending them to server, if user did not set it, + * default value for parameter will be returned + * example1: paidClicks: '[PAID_TRACKING_PIXEL]', will return {value: '', fromUser: false} + * example2: pageURL: 'www.my6sense.com', will return {value: 'www.my6sense.com', fromUser: true} + * @param key + * @param value + * @returns {{value: *, fromUser: boolean}} + */ +function fixRequestParamForServer(key, value) { + function isEmptyValue(key, value) { + return value === parametersMap[key].emptyValue; + } + + const parametersMap = { + 'pageUrl': { + emptyValue: '[PAGE_URL]', + defaultValue: getUrl() + }, + 'displayWithinIframe': { + emptyValue: '', + defaultValue: '' + }, + 'dataParams': { + emptyValue: '[KEY_VALUES]', + defaultValue: '' + }, + 'paidClicks': { + emptyValue: '[PAID_TRACKING_PIXEL]', + defaultValue: '' + }, + 'organicClicks': { + emptyValue: '[ORGANIC_TRACKING_PIXEL]', + defaultValue: '' + }, + 'dataView': { + emptyValue: '[VIEW_TRACKING_PIXEL]', + defaultValue: '' + }, + // ZONE is not part of this object, handled on server side + }; + + // if param is not in list we do not change it (return it as is) + if (!parametersMap.hasOwnProperty(key)) { + return { + value: value, + fromUser: true + }; + } + + // if no value given by user set it to default + if (!value || isEmptyValue(key, value)) { + return { + value: parametersMap[key].defaultValue, + fromUser: false + }; + } + + return { + value: value, + fromUser: true + }; +} + +// called second + +function buildGdprServerProperty(bidderRequest) { + var gdprObj = { + gdpr_consent: null, + gdpr: null + }; + + if (bidderRequest && 'gdprConsent' in bidderRequest) { + gdprObj.gdpr_consent = bidderRequest.gdprConsent.consentString || null; + + gdprObj.gdpr = gdprObj.gdpr === null && bidderRequest.gdprConsent.gdprApplies == true ? true : gdprObj.gdpr; + gdprObj.gdpr = gdprObj.gdpr === null && bidderRequest.gdprConsent.gdprApplies == false ? false : gdprObj.gdpr; + gdprObj.gdpr = gdprObj.gdpr === null && bidderRequest.gdprConsent.gdprApplies == 1 ? true : gdprObj.gdpr; + gdprObj.gdpr = gdprObj.gdpr === null && bidderRequest.gdprConsent.gdprApplies == 0 ? false : gdprObj.gdpr; + } + + return gdprObj; +} + +function buildRequests(validBidRequests, bidderRequest) { + let requests = []; + + if (validBidRequests && validBidRequests.length) { + validBidRequests.forEach(bidRequest => { + bidRequest.widget_num = 1; // mandatory property for server side + let isDataUrlSetByUser = false; + let debug = false; + + if (bidRequest.params) { + for (let key in bidRequest.params) { + // loop over params and remove empty/untouched values + if (bidRequest.params.hasOwnProperty(key)) { + // if debug we update url string to get core debug version + if (key === 'debug' && bidRequest.params[key] === true) { + debug = true; + delete bidRequest.params[key]; + continue; + } + + let fixedObj = fixRequestParamForServer(key, bidRequest.params[key]); + bidRequest.params[key] = fixedObj.value; + + // if pageUrl is set by user we should update variable for query string param + if (key === 'pageUrl' && fixedObj.fromUser === true) { + isDataUrlSetByUser = true; + } + + // remove empty params from request + if (!bidRequest.params[key]) { + delete bidRequest.params[key]; + } + } + } + } + + let url = `${END_POINT}?widget_key=${bidRequest.params.key}&is_data_url_set=${isDataUrlSetByUser}`; // mandatory query string for server side + if (debug) { + url = `${END_POINT}?env=debug&widget_key=${bidRequest.params.key}&is_data_url_set=${isDataUrlSetByUser}`; // this url is for debugging + } + + bidRequest.gdpr = buildGdprServerProperty(bidderRequest); + + requests.push({ + url: url, + method: END_POINT_METHOD, + data: JSON.stringify(bidRequest) + }); + }); + } + + return requests; +} + +// called third + +function interpretResponse(serverResponse) { + const bidResponses = []; + // currently server returns a single response which is the body property + if (serverResponse.body) { + serverResponse.body.bidderCode = BIDDER_CODE; + bidResponses.push(serverResponse.body); + } + + return bidResponses; +} + +const spec = { + code: BIDDER_CODE, + isBidRequestValid, + buildRequests, + interpretResponse +}; + +registerBidder(spec); + +module.exports = spec; diff --git a/modules/my6senseBidAdapter.md b/modules/my6senseBidAdapter.md new file mode 100644 index 00000000000..bdda690cf3f --- /dev/null +++ b/modules/my6senseBidAdapter.md @@ -0,0 +1,35 @@ +# HeaderBiddingAdapter + +# Overview + +``` +Module Name: my6sense Bidder Adapter +Module Type: Bidder Adapter +``` + +# Description + +Module that connects to my6sense demand sources. +Banner formats are supported. + +# Test Parameters +``` + var adUnits = [ + + // { + // + // sizes: [[1000, 600]], + // bids: [{ + // bidder: 'my6sense', + // params: { + // key: 'mW3mLht0kMT', + // dataVersion: 3, + // pageUrl: '', + // zone: '[ZONE]', + // + // } + // }] + // } + ]; + +``` diff --git a/test/spec/modules/my6senseBidAdapter_spec.js b/test/spec/modules/my6senseBidAdapter_spec.js new file mode 100644 index 00000000000..ec8389acbb3 --- /dev/null +++ b/test/spec/modules/my6senseBidAdapter_spec.js @@ -0,0 +1,151 @@ +import { expect } from 'chai'; +import spec from 'modules/my6senseBidAdapter'; + +describe('My6sense Bid adapter test', () => { + let bidRequests, serverResponses; + beforeEach(() => { + bidRequests = [ + { + // valid 1 + bidder: 'my6sense', + params: { + key: 'DTAeOJN67pCjY36dbhrM3G', + dataVersion: 3, + pageUrl: 'liran.com', + zone: '[ZONE]', + dataParams: '', + dataView: '', + organicClicks: '', + paidClicks: '' + } + }, + { + // invalid 2- no params + bidder: 'my6sense' + }, + { + // invalid 3 - no key in params + bidder: 'my6sense', + params: { + dataVersion: 3, + pageUrl: 'liran.com', + zone: '[ZONE]', + dataParams: '', + dataView: '', + organicClicks: '', + paidClicks: '' + } + }, + { + // invalid 3 - wrong bidder name + bidder: 'test', + params: { + key: 'ZxA0bNhlO9tf5EZ1Q9ZYdS', + dataVersion: 3, + pageUrl: 'liran.com', + zone: '[ZONE]', + dataParams: '', + dataView: '', + organicClicks: '', + paidClicks: '' + } + } + ]; + serverResponses = [ + { + headers: {}, + body: { + cpm: 1.5, + width: 300, + height: 250, + placement_id: 1, + adm: '' + } + }, + { + headers: {}, + body: { + cpm: 0, + width: 0, + height: 0, + placement_id: 1, + adm: '' + } + }, + { + headers: {}, + body: { + cpm: 0, + width: 0, + height: 0, + placement_id: 0, + adm: '' + } + }, + { + headers: {}, + body: { + cpm: 5, + creativeId: '5b29f5d1e4b086e3ee8de36b', + currency: 'USD', + height: 250, + netRevenue: false, + requestId: '2954a0957643bb', + ttl: 360, + width: 300, + adm: '' + } + } + ] + }); + + describe('test if requestIsValid function', () => { + it('with valid data 1', () => { + expect(spec.isBidRequestValid(bidRequests[0])).to.equal(true); + }); + it('with invalid data 2', () => { + expect(spec.isBidRequestValid(bidRequests[1])).to.equal(false); + }); + it('with invalid data 3', () => { + expect(spec.isBidRequestValid(bidRequests[2])).to.equal(false); + }); + it('with invalid data 3', () => { + expect(spec.isBidRequestValid(bidRequests[3])).to.equal(false); + }); + }); + + describe('test if buildRequests function', () => { + it('normal', () => { + var requests = spec.buildRequests([bidRequests[0]]); + expect(requests).to.be.lengthOf(1); + }); + }); + describe('test bid responses', () => { + it('success 1', () => { + var bids = spec.interpretResponse(serverResponses[0], {'bidRequest': bidRequests[0]}); + expect(bids).to.be.lengthOf(1); + expect(bids[0].cpm).to.equal(1.5); + expect(bids[0].width).to.equal(300); + expect(bids[0].height).to.equal(250); + expect(bids[0].adm).to.have.length.above(1); + }); + it('success 2', () => { + var bids = spec.interpretResponse(serverResponses[3]); + expect(bids).to.be.lengthOf(1); + expect(bids[0].cpm).to.equal(5); + expect(bids[0].width).to.equal(300); + expect(bids[0].height).to.equal(250); + expect(bids[0].netRevenue).to.equal(false); + expect(bids[0].ttl).to.equal(360); + expect(bids[0].currency).to.equal('USD'); + }); + it('fail 1 (cpm=0)', () => { + var bids = spec.interpretResponse(serverResponses[1]); + expect(bids).to.be.lengthOf(1); + }); + it('fail 2 (no response)', () => { + var bids = spec.interpretResponse([]); + expect(bids).to.be.lengthOf(0); + }); + }); +}); From 3ca45c608460ee69a218f36a5f3c413572be87a6 Mon Sep 17 00:00:00 2001 From: Ziv Bruchian Date: Wed, 20 Jun 2018 14:52:14 +0300 Subject: [PATCH 2/7] endpoint fix --- modules/my6senseBidAdapter.js | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/modules/my6senseBidAdapter.js b/modules/my6senseBidAdapter.js index b6d12777efe..6e816b30afb 100644 --- a/modules/my6senseBidAdapter.js +++ b/modules/my6senseBidAdapter.js @@ -3,12 +3,7 @@ require('babel-core/register')({ }); const {registerBidder} = require('../src/adapters/bidderFactory'); const BIDDER_CODE = 'my6sense'; -// production -// const END_POINT = 'http://papi.mynativeplatform.com/pub2/web/hbwidget.json'; -// local -const END_POINT = 'http://127.0.0.1:8080/pub2/web/hbwidget.json'; -// Qa -// const END_POINT = 'http://54.237.134.160:8080/pub2/web/hbwidget.json'; +const END_POINT = 'http://papi.mynativeplatform.com/pub2/web/hbwidget.json'; const END_POINT_METHOD = 'POST'; // called first From b66cb05e9b7513b9824c5decf74bc1334ac5517c Mon Sep 17 00:00:00 2001 From: Ziv Bruchian Date: Wed, 20 Jun 2018 14:58:25 +0300 Subject: [PATCH 3/7] Code fix --- modules/my6senseBidAdapter.js | 3 --- 1 file changed, 3 deletions(-) diff --git a/modules/my6senseBidAdapter.js b/modules/my6senseBidAdapter.js index 6e816b30afb..95a279b170f 100644 --- a/modules/my6senseBidAdapter.js +++ b/modules/my6senseBidAdapter.js @@ -1,6 +1,3 @@ -require('babel-core/register')({ - ignore: /node_modules\/(?!ProjectB)/ -}); const {registerBidder} = require('../src/adapters/bidderFactory'); const BIDDER_CODE = 'my6sense'; const END_POINT = 'http://papi.mynativeplatform.com/pub2/web/hbwidget.json'; From cf929858807a91a8e466fb05bcb198a5f0eb4bc7 Mon Sep 17 00:00:00 2001 From: Ziv Bruchian Date: Sun, 24 Jun 2018 11:50:16 +0300 Subject: [PATCH 4/7] Added changes in adapter md file --- modules/my6senseBidAdapter.md | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/modules/my6senseBidAdapter.md b/modules/my6senseBidAdapter.md index bdda690cf3f..96d2b543fd0 100644 --- a/modules/my6senseBidAdapter.md +++ b/modules/my6senseBidAdapter.md @@ -23,10 +23,11 @@ Banner formats are supported. // bidder: 'my6sense', // params: { // key: 'mW3mLht0kMT', - // dataVersion: 3, - // pageUrl: '', + // pageUrl: '[PAGE_URL]', // zone: '[ZONE]', - // + // dataView: '' + // organicClicks:'', + // paidClicks:'', // } // }] // } From a386643491bd862e56653ec213101cd1bfabf26f Mon Sep 17 00:00:00 2001 From: Ziv Bruchian Date: Tue, 26 Jun 2018 10:51:44 +0300 Subject: [PATCH 5/7] Changed the end point --- modules/my6senseBidAdapter.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/my6senseBidAdapter.js b/modules/my6senseBidAdapter.js index 95a279b170f..bf88a019657 100644 --- a/modules/my6senseBidAdapter.js +++ b/modules/my6senseBidAdapter.js @@ -1,6 +1,6 @@ const {registerBidder} = require('../src/adapters/bidderFactory'); const BIDDER_CODE = 'my6sense'; -const END_POINT = 'http://papi.mynativeplatform.com/pub2/web/hbwidget.json'; +const END_POINT = '//papi.mynativeplatform.com/pub2/web/v1.15.0/hbwidget.json'; const END_POINT_METHOD = 'POST'; // called first From 72e98b776ab4dbdfedc87dda52caa4aa05e62ab0 Mon Sep 17 00:00:00 2001 From: Sahar Alkolombra Date: Mon, 9 Jul 2018 07:50:49 +0300 Subject: [PATCH 6/7] supportedMediaTypes values added --- modules/my6senseBidAdapter.js | 3 +++ 1 file changed, 3 insertions(+) diff --git a/modules/my6senseBidAdapter.js b/modules/my6senseBidAdapter.js index bf88a019657..5796d1a0383 100644 --- a/modules/my6senseBidAdapter.js +++ b/modules/my6senseBidAdapter.js @@ -1,3 +1,5 @@ +import { BANNER, NATIVE } from 'src/mediaTypes'; + const {registerBidder} = require('../src/adapters/bidderFactory'); const BIDDER_CODE = 'my6sense'; const END_POINT = '//papi.mynativeplatform.com/pub2/web/v1.15.0/hbwidget.json'; @@ -184,6 +186,7 @@ function interpretResponse(serverResponse) { const spec = { code: BIDDER_CODE, + supportedMediaTypes: [BANNER, NATIVE], isBidRequestValid, buildRequests, interpretResponse From 88c298e4263edc89bf397a9033fb19cdda380c9b Mon Sep 17 00:00:00 2001 From: Sahar Alkolombra Date: Tue, 10 Jul 2018 10:28:36 +0300 Subject: [PATCH 7/7] md file was updated with a valid widget key --- modules/my6senseBidAdapter.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/my6senseBidAdapter.md b/modules/my6senseBidAdapter.md index 96d2b543fd0..7f422e6fabf 100644 --- a/modules/my6senseBidAdapter.md +++ b/modules/my6senseBidAdapter.md @@ -22,7 +22,7 @@ Banner formats are supported. // bids: [{ // bidder: 'my6sense', // params: { - // key: 'mW3mLht0kMT', + // key: 'OAJJBW2LRYi2CxfhzqogkA', // pageUrl: '[PAGE_URL]', // zone: '[ZONE]', // dataView: ''