From 5e01572be7b9583b542e532c46c2492446736be9 Mon Sep 17 00:00:00 2001 From: Aiholkin Date: Mon, 5 Oct 2020 14:43:12 +0300 Subject: [PATCH 01/11] inital --- modules/krushmediaBidAdapter.js | 103 +++++++ modules/krushmediaBidAdapter.md | 55 ++++ .../spec/modules/krushmediaBidAdapter_spec.js | 270 ++++++++++++++++++ 3 files changed, 428 insertions(+) create mode 100644 modules/krushmediaBidAdapter.js create mode 100644 modules/krushmediaBidAdapter.md create mode 100644 test/spec/modules/krushmediaBidAdapter_spec.js diff --git a/modules/krushmediaBidAdapter.js b/modules/krushmediaBidAdapter.js new file mode 100644 index 00000000000..ac889f1cd2b --- /dev/null +++ b/modules/krushmediaBidAdapter.js @@ -0,0 +1,103 @@ +import {registerBidder} from '../src/adapters/bidderFactory.js'; +import { BANNER, NATIVE, VIDEO } from '../src/mediaTypes.js'; +import * as utils from '../src/utils.js'; + +const BIDDER_CODE = 'krushmedia'; +const AD_URL = 'https://ads4.krushmedia.com/?c=rtb&m=hb'; + +function isBidResponseValid(bid) { + if (!bid.requestId || !bid.cpm || !bid.creativeId || + !bid.ttl || !bid.currency) { + return false; + } + switch (bid.mediaType) { + case BANNER: + return Boolean(bid.width && bid.height && bid.ad); + case VIDEO: + return Boolean(bid.vastUrl); + case NATIVE: + return Boolean(bid.native && bid.native.title && bid.native.image && bid.native.impressionTrackers); + default: + return false; + } +} + +export const spec = { + code: BIDDER_CODE, + supportedMediaTypes: [BANNER, VIDEO, NATIVE], + + isBidRequestValid: (bid) => { + return Boolean(bid.bidId && bid.params && !isNaN(parseInt(bid.params.key))); + }, + + buildRequests: (validBidRequests = [], bidderRequest) => { + let winTop = window; + let location; + try { + location = new URL(bidderRequest.refererInfo.referer) + winTop = window.top; + } catch (e) { + location = winTop.location; + utils.logMessage(e); + }; + let placements = []; + let request = { + 'deviceWidth': winTop.screen.width, + 'deviceHeight': winTop.screen.height, + 'language': (navigator && navigator.language) ? navigator.language.split('-')[0] : '', + 'secure': 1, + 'host': location.host, + 'page': location.pathname, + 'placements': placements + }; + if (bidderRequest) { + if (bidderRequest.uspConsent) { + request.ccpa = bidderRequest.uspConsent; + } + if (bidderRequest.gdprConsent) { + request.gdpr = bidderRequest.gdprConsent + } + } + const len = validBidRequests.length; + + for (let i = 0; i < len; i++) { + let bid = validBidRequests[i]; + let sizes + if (bid.mediaTypes) { + if (bid.mediaTypes[BANNER] && bid.mediaTypes[BANNER].sizes) { + sizes = bid.mediaTypes[BANNER].sizes + } else if (bid.mediaTypes[VIDEO] && bid.mediaTypes[VIDEO].playerSize) { + sizes = bid.mediaTypes[VIDEO].playerSize + } + } + + placements.push({ + key: bid.params.key, + bidId: bid.bidId, + sizes: sizes || [], + wPlayer: sizes ? sizes[0] : 0, + hPlayer: sizes ? sizes[1] : 0, + traffic: bid.params.traffic || BANNER, + schain: bid.schain || {}, + }); + } + return { + method: 'POST', + url: AD_URL, + data: request + }; + }, + + interpretResponse: (serverResponse) => { + let response = []; + for (let i = 0; i < serverResponse.body.length; i++) { + let resItem = serverResponse.body[i]; + if (isBidResponseValid(resItem)) { + response.push(resItem); + } + } + return response; + }, +}; + +registerBidder(spec); diff --git a/modules/krushmediaBidAdapter.md b/modules/krushmediaBidAdapter.md new file mode 100644 index 00000000000..47122897859 --- /dev/null +++ b/modules/krushmediaBidAdapter.md @@ -0,0 +1,55 @@ +# Overview + +``` +Module Name: krushmedia Bidder Adapter +Module Type: krushmedia Bidder Adapter +``` + +# Description + +Module that connects to krushmedia demand sources + +# Test Parameters +``` + var adUnits = [ + // Will return static test banner + { + code: 'adunit1', + mediaTypes: { + banner: { + sizes: [[300, 250]], + } + }, + bids: [ + { + bidder: 'krushmedia', + params: { + key: 783, + traffic: 'banner' + } + } + ] + }, + // Will return test vast xml. All video params are stored under placement in publishers UI + { + code: 'addunit2', + mediaTypes: { + video: { + playerSize: [640, 480], + context: 'instream' + } + }, + bids: [ + { + bidder: 'krushmedia', + params: { + key: 783, + traffic: 'video', + keywords: ['cat_1', 'cat_2'] + + } + } + ] + } + ]; +``` diff --git a/test/spec/modules/krushmediaBidAdapter_spec.js b/test/spec/modules/krushmediaBidAdapter_spec.js new file mode 100644 index 00000000000..b10167d801b --- /dev/null +++ b/test/spec/modules/krushmediaBidAdapter_spec.js @@ -0,0 +1,270 @@ +import {expect} from 'chai'; +import {spec} from '../../../modules/krushmediaBidAdapter.js'; +import { BANNER, VIDEO } from '../../../src/mediaTypes.js'; + +describe('KrushmediabBidAdapter', function () { + const bid = { + bidId: '23fhj33i987f', + bidder: 'krushmedia', + params: { + key: 783, + traffic: BANNER + } + }; + + const bidderRequest = { + refererInfo: { + referer: 'test.com' + } + }; + + describe('isBidRequestValid', function () { + it('Should return true if there are bidId, params and key parameters present', function () { + expect(spec.isBidRequestValid(bid)).to.be.true; + }); + it('Should return false if at least one of parameters is not present', function () { + delete bid.params.key; + expect(spec.isBidRequestValid(bid)).to.be.false; + }); + }); + + describe('buildRequests', function () { + let serverRequest = spec.buildRequests([bid], bidderRequest); + it('Creates a ServerRequest object with method, URL and data', function () { + expect(serverRequest).to.exist; + expect(serverRequest.method).to.exist; + expect(serverRequest.url).to.exist; + expect(serverRequest.data).to.exist; + }); + it('Returns POST method', function () { + expect(serverRequest.method).to.equal('POST'); + }); + it('Returns valid URL', function () { + expect(serverRequest.url).to.equal('https://ads4.krushmedia.com/?c=rtb&m=hb'); + }); + it('Returns valid data if array of bids is valid', function () { + let data = serverRequest.data; + expect(data).to.be.an('object'); + expect(data).to.have.all.keys('deviceWidth', 'deviceHeight', 'language', 'secure', 'host', 'page', 'placements'); + expect(data.deviceWidth).to.be.a('number'); + expect(data.deviceHeight).to.be.a('number'); + expect(data.language).to.be.a('string'); + expect(data.secure).to.be.within(0, 1); + expect(data.host).to.be.a('string'); + expect(data.page).to.be.a('string'); + expect(data.gdpr).to.not.exist; + expect(data.ccpa).to.not.exist; + let placement = data['placements'][0]; + expect(placement).to.have.keys('key', 'bidId', 'traffic', 'sizes', 'hPlayer', 'wPlayer', 'schain'); + expect(placement.key).to.equal(0); + expect(placement.bidId).to.equal('23fhj33i987f'); + expect(placement.traffic).to.equal(BANNER); + expect(placement.schain).to.be.an('object'); + }); + + it('Returns valid data for mediatype video', function () { + const playerSize = [300, 300]; + bid.mediaTypes = {}; + bid.params.traffic = VIDEO; + bid.mediaTypes[VIDEO] = { + playerSize + }; + serverRequest = spec.buildRequests([bid], bidderRequest); + let data = serverRequest.data; + expect(data).to.be.an('object'); + let placement = data['placements'][0]; + expect(placement).to.be.an('object'); + expect(placement.traffic).to.equal(VIDEO); + expect(placement.wPlayer).to.equal(playerSize[0]); + expect(placement.hPlayer).to.equal(playerSize[1]); + }); + + it('Returns data with gdprConsent and without uspConsent', function () { + bidderRequest.gdprConsent = 'test'; + serverRequest = spec.buildRequests([bid], bidderRequest); + let data = serverRequest.data; + expect(data.gdpr).to.exist; + expect(data.gdpr).to.be.a('string'); + expect(data.gdpr).to.equal(bidderRequest.gdprConsent); + expect(data.ccpa).to.not.exist; + delete bidderRequest.gdprConsent; + }); + + it('Returns data with uspConsent and without gdprConsent', function () { + bidderRequest.uspConsent = 'test'; + serverRequest = spec.buildRequests([bid], bidderRequest); + let data = serverRequest.data; + expect(data.ccpa).to.exist; + expect(data.ccpa).to.be.a('string'); + expect(data.ccpa).to.equal(bidderRequest.uspConsent); + expect(data.gdpr).to.not.exist; + }); + + it('Returns empty data if no valid requests are passed', function () { + serverRequest = spec.buildRequests([]); + let data = serverRequest.data; + expect(data.placements).to.be.an('array').that.is.empty; + }); + }); + describe('interpretResponse', function () { + it('Should interpret banner response', function () { + const banner = { + body: [{ + mediaType: 'banner', + width: 300, + height: 250, + cpm: 0.4, + ad: 'Test', + requestId: '23fhj33i987f', + ttl: 120, + creativeId: '2', + netRevenue: true, + currency: 'USD', + dealId: '1' + }] + }; + let bannerResponses = spec.interpretResponse(banner); + expect(bannerResponses).to.be.an('array').that.is.not.empty; + let dataItem = bannerResponses[0]; + expect(dataItem).to.have.all.keys('requestId', 'cpm', 'width', 'height', 'ad', 'ttl', 'creativeId', + 'netRevenue', 'currency', 'dealId', 'mediaType'); + expect(dataItem.requestId).to.equal('23fhj33i987f'); + expect(dataItem.cpm).to.equal(0.4); + expect(dataItem.width).to.equal(300); + expect(dataItem.height).to.equal(250); + expect(dataItem.ad).to.equal('Test'); + expect(dataItem.ttl).to.equal(120); + expect(dataItem.creativeId).to.equal('2'); + expect(dataItem.netRevenue).to.be.true; + expect(dataItem.currency).to.equal('USD'); + }); + it('Should interpret video response', function () { + const video = { + body: [{ + vastUrl: 'test.com', + mediaType: 'video', + cpm: 0.5, + requestId: '23fhj33i987f', + ttl: 120, + creativeId: '2', + netRevenue: true, + currency: 'USD', + dealId: '1' + }] + }; + let videoResponses = spec.interpretResponse(video); + expect(videoResponses).to.be.an('array').that.is.not.empty; + + let dataItem = videoResponses[0]; + expect(dataItem).to.have.all.keys('requestId', 'cpm', 'vastUrl', 'ttl', 'creativeId', + 'netRevenue', 'currency', 'dealId', 'mediaType'); + expect(dataItem.requestId).to.equal('23fhj33i987f'); + expect(dataItem.cpm).to.equal(0.5); + expect(dataItem.vastUrl).to.equal('test.com'); + expect(dataItem.ttl).to.equal(120); + expect(dataItem.creativeId).to.equal('2'); + expect(dataItem.netRevenue).to.be.true; + expect(dataItem.currency).to.equal('USD'); + }); + it('Should interpret native response', function () { + const native = { + body: [{ + mediaType: 'native', + native: { + clickUrl: 'test.com', + title: 'Test', + image: 'test.com', + impressionTrackers: ['test.com'], + }, + ttl: 120, + cpm: 0.4, + requestId: '23fhj33i987f', + creativeId: '2', + netRevenue: true, + currency: 'USD', + }] + }; + let nativeResponses = spec.interpretResponse(native); + expect(nativeResponses).to.be.an('array').that.is.not.empty; + + let dataItem = nativeResponses[0]; + expect(dataItem).to.have.keys('requestId', 'cpm', 'ttl', 'creativeId', 'netRevenue', 'currency', 'mediaType', 'native'); + expect(dataItem.native).to.have.keys('clickUrl', 'impressionTrackers', 'title', 'image') + expect(dataItem.requestId).to.equal('23fhj33i987f'); + expect(dataItem.cpm).to.equal(0.4); + expect(dataItem.native.clickUrl).to.equal('test.com'); + expect(dataItem.native.title).to.equal('Test'); + expect(dataItem.native.image).to.equal('test.com'); + expect(dataItem.native.impressionTrackers).to.be.an('array').that.is.not.empty; + expect(dataItem.native.impressionTrackers[0]).to.equal('test.com'); + expect(dataItem.ttl).to.equal(120); + expect(dataItem.creativeId).to.equal('2'); + expect(dataItem.netRevenue).to.be.true; + expect(dataItem.currency).to.equal('USD'); + }); + it('Should return an empty array if invalid banner response is passed', function () { + const invBanner = { + body: [{ + width: 300, + cpm: 0.4, + ad: 'Test', + requestId: '23fhj33i987f', + ttl: 120, + creativeId: '2', + netRevenue: true, + currency: 'USD', + dealId: '1' + }] + }; + + let serverResponses = spec.interpretResponse(invBanner); + expect(serverResponses).to.be.an('array').that.is.empty; + }); + it('Should return an empty array if invalid video response is passed', function () { + const invVideo = { + body: [{ + mediaType: 'video', + cpm: 0.5, + requestId: '23fhj33i987f', + ttl: 120, + creativeId: '2', + netRevenue: true, + currency: 'USD', + dealId: '1' + }] + }; + let serverResponses = spec.interpretResponse(invVideo); + expect(serverResponses).to.be.an('array').that.is.empty; + }); + it('Should return an empty array if invalid native response is passed', function () { + const invNative = { + body: [{ + mediaType: 'native', + clickUrl: 'test.com', + title: 'Test', + impressionTrackers: ['test.com'], + ttl: 120, + requestId: '23fhj33i987f', + creativeId: '2', + netRevenue: true, + currency: 'USD', + }] + }; + let serverResponses = spec.interpretResponse(invNative); + expect(serverResponses).to.be.an('array').that.is.empty; + }); + it('Should return an empty array if invalid response is passed', function () { + const invalid = { + body: [{ + ttl: 120, + creativeId: '2', + netRevenue: true, + currency: 'USD', + dealId: '1' + }] + }; + let serverResponses = spec.interpretResponse(invalid); + expect(serverResponses).to.be.an('array').that.is.empty; + }); + }); +}); From f00b280f899fadb270d51436a8c2f425661e0231 Mon Sep 17 00:00:00 2001 From: Aiholkin Date: Wed, 7 Oct 2020 17:23:37 +0300 Subject: [PATCH 02/11] fix --- test/spec/modules/krushmediaBidAdapter_spec.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/spec/modules/krushmediaBidAdapter_spec.js b/test/spec/modules/krushmediaBidAdapter_spec.js index b10167d801b..b86e83def4e 100644 --- a/test/spec/modules/krushmediaBidAdapter_spec.js +++ b/test/spec/modules/krushmediaBidAdapter_spec.js @@ -56,7 +56,7 @@ describe('KrushmediabBidAdapter', function () { expect(data.ccpa).to.not.exist; let placement = data['placements'][0]; expect(placement).to.have.keys('key', 'bidId', 'traffic', 'sizes', 'hPlayer', 'wPlayer', 'schain'); - expect(placement.key).to.equal(0); + expect(placement.key).to.equal(783); expect(placement.bidId).to.equal('23fhj33i987f'); expect(placement.traffic).to.equal(BANNER); expect(placement.schain).to.be.an('object'); From 08c8378d231948ead2230ff03f3d864640736834 Mon Sep 17 00:00:00 2001 From: Aiholkin Date: Wed, 7 Oct 2020 17:26:14 +0300 Subject: [PATCH 03/11] fix --- modules/krushmediaBidAdapter.md | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/modules/krushmediaBidAdapter.md b/modules/krushmediaBidAdapter.md index 47122897859..f712b85cd01 100644 --- a/modules/krushmediaBidAdapter.md +++ b/modules/krushmediaBidAdapter.md @@ -44,9 +44,7 @@ Module that connects to krushmedia demand sources bidder: 'krushmedia', params: { key: 783, - traffic: 'video', - keywords: ['cat_1', 'cat_2'] - + traffic: 'video' } } ] From afa8c19b3892d639debfbed160380246c0150d0e Mon Sep 17 00:00:00 2001 From: Aiholkin Date: Wed, 7 Oct 2020 17:28:44 +0300 Subject: [PATCH 04/11] fix --- modules/krushmediaBidAdapter.js | 1 + modules/krushmediaBidAdapter.md | 6 ++++-- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/modules/krushmediaBidAdapter.js b/modules/krushmediaBidAdapter.js index ac889f1cd2b..9b40b72c962 100644 --- a/modules/krushmediaBidAdapter.js +++ b/modules/krushmediaBidAdapter.js @@ -73,6 +73,7 @@ export const spec = { placements.push({ key: bid.params.key, + test: bid.params.test || 0, bidId: bid.bidId, sizes: sizes || [], wPlayer: sizes ? sizes[0] : 0, diff --git a/modules/krushmediaBidAdapter.md b/modules/krushmediaBidAdapter.md index f712b85cd01..3e6d93c7bcf 100644 --- a/modules/krushmediaBidAdapter.md +++ b/modules/krushmediaBidAdapter.md @@ -25,7 +25,8 @@ Module that connects to krushmedia demand sources bidder: 'krushmedia', params: { key: 783, - traffic: 'banner' + traffic: 'banner', + test: 1 } } ] @@ -44,7 +45,8 @@ Module that connects to krushmedia demand sources bidder: 'krushmedia', params: { key: 783, - traffic: 'video' + traffic: 'video', + test: 1 } } ] From 4ada5be161c4a08d9e502f44cbc9cea8348643e2 Mon Sep 17 00:00:00 2001 From: Aiholkin Date: Wed, 7 Oct 2020 20:09:22 +0300 Subject: [PATCH 05/11] fix --- test/spec/modules/krushmediaBidAdapter_spec.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/spec/modules/krushmediaBidAdapter_spec.js b/test/spec/modules/krushmediaBidAdapter_spec.js index b86e83def4e..077c826bc42 100644 --- a/test/spec/modules/krushmediaBidAdapter_spec.js +++ b/test/spec/modules/krushmediaBidAdapter_spec.js @@ -55,7 +55,7 @@ describe('KrushmediabBidAdapter', function () { expect(data.gdpr).to.not.exist; expect(data.ccpa).to.not.exist; let placement = data['placements'][0]; - expect(placement).to.have.keys('key', 'bidId', 'traffic', 'sizes', 'hPlayer', 'wPlayer', 'schain'); + expect(placement).to.have.keys('key', 'test', 'bidId', 'traffic', 'sizes', 'hPlayer', 'wPlayer', 'schain'); expect(placement.key).to.equal(783); expect(placement.bidId).to.equal('23fhj33i987f'); expect(placement.traffic).to.equal(BANNER); From 84d7a38017ac0cf8be034b19c19a2a3d5ebbf61e Mon Sep 17 00:00:00 2001 From: Aiholkin Date: Thu, 8 Oct 2020 15:17:56 +0300 Subject: [PATCH 06/11] fix --- modules/krushmediaBidAdapter.md | 10 ++++------ test/spec/modules/krushmediaBidAdapter_spec.js | 2 +- 2 files changed, 5 insertions(+), 7 deletions(-) diff --git a/modules/krushmediaBidAdapter.md b/modules/krushmediaBidAdapter.md index 3e6d93c7bcf..aa13b3bce39 100644 --- a/modules/krushmediaBidAdapter.md +++ b/modules/krushmediaBidAdapter.md @@ -24,9 +24,8 @@ Module that connects to krushmedia demand sources { bidder: 'krushmedia', params: { - key: 783, - traffic: 'banner', - test: 1 + key: 0, + traffic: 'banner' } } ] @@ -44,9 +43,8 @@ Module that connects to krushmedia demand sources { bidder: 'krushmedia', params: { - key: 783, - traffic: 'video', - test: 1 + key: 0, + traffic: 'video' } } ] diff --git a/test/spec/modules/krushmediaBidAdapter_spec.js b/test/spec/modules/krushmediaBidAdapter_spec.js index 077c826bc42..b86e83def4e 100644 --- a/test/spec/modules/krushmediaBidAdapter_spec.js +++ b/test/spec/modules/krushmediaBidAdapter_spec.js @@ -55,7 +55,7 @@ describe('KrushmediabBidAdapter', function () { expect(data.gdpr).to.not.exist; expect(data.ccpa).to.not.exist; let placement = data['placements'][0]; - expect(placement).to.have.keys('key', 'test', 'bidId', 'traffic', 'sizes', 'hPlayer', 'wPlayer', 'schain'); + expect(placement).to.have.keys('key', 'bidId', 'traffic', 'sizes', 'hPlayer', 'wPlayer', 'schain'); expect(placement.key).to.equal(783); expect(placement.bidId).to.equal('23fhj33i987f'); expect(placement.traffic).to.equal(BANNER); From cef6e93fff210a72a2bf8cf55bf35e4fe41c4cb8 Mon Sep 17 00:00:00 2001 From: Aiholkin Date: Thu, 8 Oct 2020 15:32:08 +0300 Subject: [PATCH 07/11] fix --- modules/krushmediaBidAdapter.js | 1 - 1 file changed, 1 deletion(-) diff --git a/modules/krushmediaBidAdapter.js b/modules/krushmediaBidAdapter.js index 9b40b72c962..ac889f1cd2b 100644 --- a/modules/krushmediaBidAdapter.js +++ b/modules/krushmediaBidAdapter.js @@ -73,7 +73,6 @@ export const spec = { placements.push({ key: bid.params.key, - test: bid.params.test || 0, bidId: bid.bidId, sizes: sizes || [], wPlayer: sizes ? sizes[0] : 0, From 452685fe041aface92b3f4a75c7c9610c9f24495 Mon Sep 17 00:00:00 2001 From: Aiholkin Date: Thu, 8 Oct 2020 15:52:13 +0300 Subject: [PATCH 08/11] add maintener to md --- modules/krushmediaBidAdapter.md | 1 + 1 file changed, 1 insertion(+) diff --git a/modules/krushmediaBidAdapter.md b/modules/krushmediaBidAdapter.md index aa13b3bce39..71ede0e6640 100644 --- a/modules/krushmediaBidAdapter.md +++ b/modules/krushmediaBidAdapter.md @@ -3,6 +3,7 @@ ``` Module Name: krushmedia Bidder Adapter Module Type: krushmedia Bidder Adapter +Maintainer: adapter@krushmedia.com ``` # Description From 27d7a4637d20650dc7b847211e6adad7452a0f3b Mon Sep 17 00:00:00 2001 From: Vlad Date: Mon, 26 Oct 2020 15:22:06 +0200 Subject: [PATCH 09/11] Added native support --- modules/krushmediaBidAdapter.js | 39 ++++++++++--------- modules/krushmediaBidAdapter.md | 26 +++++++++++++ .../spec/modules/krushmediaBidAdapter_spec.js | 38 +++++++++++++++++- 3 files changed, 82 insertions(+), 21 deletions(-) diff --git a/modules/krushmediaBidAdapter.js b/modules/krushmediaBidAdapter.js index ac889f1cd2b..f70500cc101 100644 --- a/modules/krushmediaBidAdapter.js +++ b/modules/krushmediaBidAdapter.js @@ -16,7 +16,7 @@ function isBidResponseValid(bid) { case VIDEO: return Boolean(bid.vastUrl); case NATIVE: - return Boolean(bid.native && bid.native.title && bid.native.image && bid.native.impressionTrackers); + return Boolean(bid.native && bid.native.impressionTrackers); default: return false; } @@ -40,8 +40,9 @@ export const spec = { location = winTop.location; utils.logMessage(e); }; - let placements = []; - let request = { + + const placements = []; + const request = { 'deviceWidth': winTop.screen.width, 'deviceHeight': winTop.screen.height, 'language': (navigator && navigator.language) ? navigator.language.split('-')[0] : '', @@ -50,6 +51,7 @@ export const spec = { 'page': location.pathname, 'placements': placements }; + if (bidderRequest) { if (bidderRequest.uspConsent) { request.ccpa = bidderRequest.uspConsent; @@ -58,29 +60,28 @@ export const spec = { request.gdpr = bidderRequest.gdprConsent } } - const len = validBidRequests.length; + const len = validBidRequests.length; for (let i = 0; i < len; i++) { - let bid = validBidRequests[i]; - let sizes - if (bid.mediaTypes) { - if (bid.mediaTypes[BANNER] && bid.mediaTypes[BANNER].sizes) { - sizes = bid.mediaTypes[BANNER].sizes - } else if (bid.mediaTypes[VIDEO] && bid.mediaTypes[VIDEO].playerSize) { - sizes = bid.mediaTypes[VIDEO].playerSize - } - } - - placements.push({ + const bid = validBidRequests[i]; + const placement = { key: bid.params.key, bidId: bid.bidId, - sizes: sizes || [], - wPlayer: sizes ? sizes[0] : 0, - hPlayer: sizes ? sizes[1] : 0, traffic: bid.params.traffic || BANNER, schain: bid.schain || {}, - }); + }; + + if (bid.mediaTypes && bid.mediaTypes[BANNER] && bid.mediaTypes[BANNER].sizes) { + placement.sizes = bid.mediaTypes[BANNER].sizes; + } else if (bid.mediaTypes && bid.mediaTypes[VIDEO] && bid.mediaTypes[VIDEO].playerSize) { + placement.wPlayer = bid.mediaTypes[VIDEO].playerSize[0]; + placement.hPlayer = bid.mediaTypes[VIDEO].playerSize[1]; + } else if (bid.mediaTypes && bid.mediaTypes[NATIVE]) { + placement.native = bid.mediaTypes[NATIVE]; + } + placements.push(placement); } + return { method: 'POST', url: AD_URL, diff --git a/modules/krushmediaBidAdapter.md b/modules/krushmediaBidAdapter.md index 71ede0e6640..7bf7c4fe491 100644 --- a/modules/krushmediaBidAdapter.md +++ b/modules/krushmediaBidAdapter.md @@ -49,6 +49,32 @@ Module that connects to krushmedia demand sources } } ] + }, + { + code: 'addunit3', + mediaTypes: { + native: { + title: { + required: true + }, + body: { + required: true + }, + icon: { + required: true, + size: [64, 64] + } + } + }, + bids: [ + { + bidder: 'krushmedia', + params: { + key: 0, + traffic: 'native' + } + } + ] } ]; ``` diff --git a/test/spec/modules/krushmediaBidAdapter_spec.js b/test/spec/modules/krushmediaBidAdapter_spec.js index b86e83def4e..2673627bc6d 100644 --- a/test/spec/modules/krushmediaBidAdapter_spec.js +++ b/test/spec/modules/krushmediaBidAdapter_spec.js @@ -1,11 +1,16 @@ import {expect} from 'chai'; import {spec} from '../../../modules/krushmediaBidAdapter.js'; -import { BANNER, VIDEO } from '../../../src/mediaTypes.js'; +import { BANNER, VIDEO, NATIVE } from '../../../src/mediaTypes.js'; describe('KrushmediabBidAdapter', function () { const bid = { bidId: '23fhj33i987f', bidder: 'krushmedia', + mediaTypes: { + [BANNER]: { + sizes: [[300, 250]] + } + }, params: { key: 783, traffic: BANNER @@ -55,11 +60,12 @@ describe('KrushmediabBidAdapter', function () { expect(data.gdpr).to.not.exist; expect(data.ccpa).to.not.exist; let placement = data['placements'][0]; - expect(placement).to.have.keys('key', 'bidId', 'traffic', 'sizes', 'hPlayer', 'wPlayer', 'schain'); + expect(placement).to.have.keys('key', 'bidId', 'traffic', 'sizes', 'schain'); expect(placement.key).to.equal(783); expect(placement.bidId).to.equal('23fhj33i987f'); expect(placement.traffic).to.equal(BANNER); expect(placement.schain).to.be.an('object'); + expect(placement.sizes).to.be.an('array'); }); it('Returns valid data for mediatype video', function () { @@ -74,11 +80,39 @@ describe('KrushmediabBidAdapter', function () { expect(data).to.be.an('object'); let placement = data['placements'][0]; expect(placement).to.be.an('object'); + expect(placement).to.have.keys('key', 'bidId', 'traffic', 'wPlayer', 'hPlayer', 'schain'); expect(placement.traffic).to.equal(VIDEO); expect(placement.wPlayer).to.equal(playerSize[0]); expect(placement.hPlayer).to.equal(playerSize[1]); }); + it('Returns valid data for mediatype native', function () { + const native = { + title: { + required: true + }, + body: { + required: true + }, + icon: { + required: true, + size: [64, 64] + } + }; + + bid.mediaTypes = {}; + bid.params.traffic = NATIVE; + bid.mediaTypes[NATIVE] = native; + serverRequest = spec.buildRequests([bid], bidderRequest); + let data = serverRequest.data; + expect(data).to.be.an('object'); + let placement = data['placements'][0]; + expect(placement).to.be.an('object'); + expect(placement).to.have.keys('key', 'bidId', 'traffic', 'native', 'schain'); + expect(placement.traffic).to.equal(NATIVE); + expect(placement.native).to.equal(native); + }); + it('Returns data with gdprConsent and without uspConsent', function () { bidderRequest.gdprConsent = 'test'; serverRequest = spec.buildRequests([bid], bidderRequest); From e4819de928205efcea2084b31ab154828a3915d1 Mon Sep 17 00:00:00 2001 From: Aiholkin Date: Mon, 30 Nov 2020 21:31:28 +0200 Subject: [PATCH 10/11] add syncing --- modules/krushmediaBidAdapter.js | 19 ++++++++++++++ .../spec/modules/krushmediaBidAdapter_spec.js | 25 +++++++++++++++++++ 2 files changed, 44 insertions(+) diff --git a/modules/krushmediaBidAdapter.js b/modules/krushmediaBidAdapter.js index f70500cc101..de1cce503e3 100644 --- a/modules/krushmediaBidAdapter.js +++ b/modules/krushmediaBidAdapter.js @@ -4,6 +4,7 @@ import * as utils from '../src/utils.js'; const BIDDER_CODE = 'krushmedia'; const AD_URL = 'https://ads4.krushmedia.com/?c=rtb&m=hb'; +const SYNC_URL = 'https://cs.krushmedia.com/html?src=pbjs' function isBidResponseValid(bid) { if (!bid.requestId || !bid.cpm || !bid.creativeId || @@ -99,6 +100,24 @@ export const spec = { } return response; }, + + getUserSyncs: (syncOptions, serverResponses, gdprConsent, uspConsent) => { + let syncUrl = SYNC_URL + if (gdprConsent && gdprConsent.consentString) { + if (typeof gdprConsent.gdprApplies === 'boolean') { + syncUrl += `&gdpr=${Number(gdprConsent.gdprApplies)}&gdpr_consent=${gdprConsent.consentString}`; + } else { + syncUrl += `&gdpr=0&gdpr_consent=${gdprConsent.consentString}`; + } + } + if (uspConsent && uspConsent.consentString) { + syncUrl += `&ccpa_consent=${uspConsent.consentString}`; + } + return [{ + type: 'iframe', + url: syncUrl + }]; + } }; registerBidder(spec); diff --git a/test/spec/modules/krushmediaBidAdapter_spec.js b/test/spec/modules/krushmediaBidAdapter_spec.js index 2673627bc6d..3af9ed64c43 100644 --- a/test/spec/modules/krushmediaBidAdapter_spec.js +++ b/test/spec/modules/krushmediaBidAdapter_spec.js @@ -301,4 +301,29 @@ describe('KrushmediabBidAdapter', function () { expect(serverResponses).to.be.an('array').that.is.empty; }); }); + describe('getUserSyncs', function() { + it('Should return array of objects with proper sync config , include GDPR', function() { + const syncData = spec.getUserSyncs({}, {}, { + consentString: 'ALL', + gdprApplies: true, + }, {}); + expect(syncData).to.be.an('array').which.is.not.empty; + expect(syncData[0]).to.be.an('object') + expect(syncData[0].type).to.be.a('string') + expect(syncData[0].type).to.equal('iframe') + expect(syncData[0].url).to.be.a('string') + expect(syncData[0].url).to.equal('https://cs.krushmedia.com/html?src=pbjs&gdpr=1&gdpr_consent=ALL') + }); + it('Should return array of objects with proper sync config , include CCPA', function() { + const syncData = spec.getUserSyncs({}, {}, {}, { + consentString: '1NNN' + }); + expect(syncData).to.be.an('array').which.is.not.empty; + expect(syncData[0]).to.be.an('object') + expect(syncData[0].type).to.be.a('string') + expect(syncData[0].type).to.equal('iframe') + expect(syncData[0].url).to.be.a('string') + expect(syncData[0].url).to.equal('https://cs.krushmedia.com/html?src=pbjs&ccpa_consent=1NNN') + }); + }); }); From e25d421e1f52bd9daba4d84b97bcf02535ca0bbe Mon Sep 17 00:00:00 2001 From: Krushmedia Date: Wed, 4 Aug 2021 21:11:22 +0300 Subject: [PATCH 11/11] updates for prebid 5 compliance --- modules/krushmediaBidAdapter.js | 49 ++++++++++++++++--- package-lock.json | 2 +- .../spec/modules/krushmediaBidAdapter_spec.js | 17 ++++--- 3 files changed, 54 insertions(+), 14 deletions(-) diff --git a/modules/krushmediaBidAdapter.js b/modules/krushmediaBidAdapter.js index de1cce503e3..db024230e2a 100644 --- a/modules/krushmediaBidAdapter.js +++ b/modules/krushmediaBidAdapter.js @@ -23,6 +23,23 @@ function isBidResponseValid(bid) { } } +function getBidFloor(bid) { + if (!utils.isFn(bid.getFloor)) { + return utils.deepAccess(bid, 'params.bidfloor', 0); + } + + try { + const bidFloor = bid.getFloor({ + currency: 'USD', + mediaType: '*', + size: '*', + }); + return bidFloor.floor; + } catch (_) { + return 0 + } +} + export const spec = { code: BIDDER_CODE, supportedMediaTypes: [BANNER, VIDEO, NATIVE], @@ -44,13 +61,13 @@ export const spec = { const placements = []; const request = { - 'deviceWidth': winTop.screen.width, - 'deviceHeight': winTop.screen.height, - 'language': (navigator && navigator.language) ? navigator.language.split('-')[0] : '', - 'secure': 1, - 'host': location.host, - 'page': location.pathname, - 'placements': placements + deviceWidth: winTop.screen.width, + deviceHeight: winTop.screen.height, + language: (navigator && navigator.language) ? navigator.language.split('-')[0] : '', + secure: 1, + host: location.host, + page: location.pathname, + placements: placements }; if (bidderRequest) { @@ -70,6 +87,7 @@ export const spec = { bidId: bid.bidId, traffic: bid.params.traffic || BANNER, schain: bid.schain || {}, + bidFloor: getBidFloor(bid) }; if (bid.mediaTypes && bid.mediaTypes[BANNER] && bid.mediaTypes[BANNER].sizes) { @@ -77,6 +95,20 @@ export const spec = { } else if (bid.mediaTypes && bid.mediaTypes[VIDEO] && bid.mediaTypes[VIDEO].playerSize) { placement.wPlayer = bid.mediaTypes[VIDEO].playerSize[0]; placement.hPlayer = bid.mediaTypes[VIDEO].playerSize[1]; + placement.minduration = bid.mediaTypes[VIDEO].minduration; + placement.maxduration = bid.mediaTypes[VIDEO].maxduration; + placement.mimes = bid.mediaTypes[VIDEO].mimes; + placement.protocols = bid.mediaTypes[VIDEO].protocols; + placement.startdelay = bid.mediaTypes[VIDEO].startdelay; + placement.placement = bid.mediaTypes[VIDEO].placement; + placement.skip = bid.mediaTypes[VIDEO].skip; + placement.skipafter = bid.mediaTypes[VIDEO].skipafter; + placement.minbitrate = bid.mediaTypes[VIDEO].minbitrate; + placement.maxbitrate = bid.mediaTypes[VIDEO].maxbitrate; + placement.delivery = bid.mediaTypes[VIDEO].delivery; + placement.playbackmethod = bid.mediaTypes[VIDEO].playbackmethod; + placement.api = bid.mediaTypes[VIDEO].api; + placement.linearity = bid.mediaTypes[VIDEO].linearity; } else if (bid.mediaTypes && bid.mediaTypes[NATIVE]) { placement.native = bid.mediaTypes[NATIVE]; } @@ -95,6 +127,9 @@ export const spec = { for (let i = 0; i < serverResponse.body.length; i++) { let resItem = serverResponse.body[i]; if (isBidResponseValid(resItem)) { + const advertiserDomains = resItem.adomain && resItem.adomain.length ? resItem.adomain : []; + resItem.meta = { ...resItem.meta, advertiserDomains }; + response.push(resItem); } } diff --git a/package-lock.json b/package-lock.json index bf95487ed9d..e9ee53305b2 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "prebid.js", - "version": "4.14.0-pre", + "version": "4.18.0-pre", "lockfileVersion": 1, "requires": true, "dependencies": { diff --git a/test/spec/modules/krushmediaBidAdapter_spec.js b/test/spec/modules/krushmediaBidAdapter_spec.js index 3af9ed64c43..fcdcc942290 100644 --- a/test/spec/modules/krushmediaBidAdapter_spec.js +++ b/test/spec/modules/krushmediaBidAdapter_spec.js @@ -60,7 +60,7 @@ describe('KrushmediabBidAdapter', function () { expect(data.gdpr).to.not.exist; expect(data.ccpa).to.not.exist; let placement = data['placements'][0]; - expect(placement).to.have.keys('key', 'bidId', 'traffic', 'sizes', 'schain'); + expect(placement).to.have.keys('key', 'bidId', 'traffic', 'sizes', 'schain', 'bidFloor'); expect(placement.key).to.equal(783); expect(placement.bidId).to.equal('23fhj33i987f'); expect(placement.traffic).to.equal(BANNER); @@ -80,7 +80,9 @@ describe('KrushmediabBidAdapter', function () { expect(data).to.be.an('object'); let placement = data['placements'][0]; expect(placement).to.be.an('object'); - expect(placement).to.have.keys('key', 'bidId', 'traffic', 'wPlayer', 'hPlayer', 'schain'); + expect(placement).to.have.keys('key', 'bidId', 'traffic', 'wPlayer', 'hPlayer', 'schain', 'bidFloor', + 'minduration', 'maxduration', 'mimes', 'protocols', 'startdelay', 'placement', 'skip', + 'skipafter', 'minbitrate', 'maxbitrate', 'delivery', 'playbackmethod', 'api', 'linearity'); expect(placement.traffic).to.equal(VIDEO); expect(placement.wPlayer).to.equal(playerSize[0]); expect(placement.hPlayer).to.equal(playerSize[1]); @@ -108,7 +110,7 @@ describe('KrushmediabBidAdapter', function () { expect(data).to.be.an('object'); let placement = data['placements'][0]; expect(placement).to.be.an('object'); - expect(placement).to.have.keys('key', 'bidId', 'traffic', 'native', 'schain'); + expect(placement).to.have.keys('key', 'bidId', 'traffic', 'native', 'schain', 'bidFloor'); expect(placement.traffic).to.equal(NATIVE); expect(placement.native).to.equal(native); }); @@ -161,7 +163,7 @@ describe('KrushmediabBidAdapter', function () { expect(bannerResponses).to.be.an('array').that.is.not.empty; let dataItem = bannerResponses[0]; expect(dataItem).to.have.all.keys('requestId', 'cpm', 'width', 'height', 'ad', 'ttl', 'creativeId', - 'netRevenue', 'currency', 'dealId', 'mediaType'); + 'netRevenue', 'currency', 'dealId', 'mediaType', 'meta'); expect(dataItem.requestId).to.equal('23fhj33i987f'); expect(dataItem.cpm).to.equal(0.4); expect(dataItem.width).to.equal(300); @@ -171,6 +173,7 @@ describe('KrushmediabBidAdapter', function () { expect(dataItem.creativeId).to.equal('2'); expect(dataItem.netRevenue).to.be.true; expect(dataItem.currency).to.equal('USD'); + expect(dataItem.meta).to.be.an('object').that.has.any.key('advertiserDomains'); }); it('Should interpret video response', function () { const video = { @@ -191,7 +194,7 @@ describe('KrushmediabBidAdapter', function () { let dataItem = videoResponses[0]; expect(dataItem).to.have.all.keys('requestId', 'cpm', 'vastUrl', 'ttl', 'creativeId', - 'netRevenue', 'currency', 'dealId', 'mediaType'); + 'netRevenue', 'currency', 'dealId', 'mediaType', 'meta'); expect(dataItem.requestId).to.equal('23fhj33i987f'); expect(dataItem.cpm).to.equal(0.5); expect(dataItem.vastUrl).to.equal('test.com'); @@ -199,6 +202,7 @@ describe('KrushmediabBidAdapter', function () { expect(dataItem.creativeId).to.equal('2'); expect(dataItem.netRevenue).to.be.true; expect(dataItem.currency).to.equal('USD'); + expect(dataItem.meta).to.be.an('object').that.has.any.key('advertiserDomains'); }); it('Should interpret native response', function () { const native = { @@ -222,7 +226,7 @@ describe('KrushmediabBidAdapter', function () { expect(nativeResponses).to.be.an('array').that.is.not.empty; let dataItem = nativeResponses[0]; - expect(dataItem).to.have.keys('requestId', 'cpm', 'ttl', 'creativeId', 'netRevenue', 'currency', 'mediaType', 'native'); + expect(dataItem).to.have.keys('requestId', 'cpm', 'ttl', 'creativeId', 'netRevenue', 'currency', 'mediaType', 'native', 'meta'); expect(dataItem.native).to.have.keys('clickUrl', 'impressionTrackers', 'title', 'image') expect(dataItem.requestId).to.equal('23fhj33i987f'); expect(dataItem.cpm).to.equal(0.4); @@ -235,6 +239,7 @@ describe('KrushmediabBidAdapter', function () { expect(dataItem.creativeId).to.equal('2'); expect(dataItem.netRevenue).to.be.true; expect(dataItem.currency).to.equal('USD'); + expect(dataItem.meta).to.be.an('object').that.has.any.key('advertiserDomains'); }); it('Should return an empty array if invalid banner response is passed', function () { const invBanner = {