From bfede800b031493e6c0336ebbd4113204e07e6c4 Mon Sep 17 00:00:00 2001 From: vlad Date: Mon, 10 May 2021 16:21:54 +0300 Subject: [PATCH 1/6] New Adapter: BidsCube --- modules/bidscubeBidAdapter.js | 90 ++++++++ modules/bidscubeBidAdapter.md | 27 +++ test/spec/modules/bidscubeBidAdapter_spec.js | 207 +++++++++++++++++++ 3 files changed, 324 insertions(+) create mode 100644 modules/bidscubeBidAdapter.js create mode 100644 modules/bidscubeBidAdapter.md create mode 100644 test/spec/modules/bidscubeBidAdapter_spec.js diff --git a/modules/bidscubeBidAdapter.js b/modules/bidscubeBidAdapter.js new file mode 100644 index 00000000000..92eeb48d83b --- /dev/null +++ b/modules/bidscubeBidAdapter.js @@ -0,0 +1,90 @@ +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 = 'bidscube' +const URL = 'https://supply.bidscube.com/?c=o&m=multi' +const URL_SYNC = 'https://supply.bidscube.com/?c=o&m=cookie' + +export const spec = { + code: BIDDER_CODE, + supportedMediaTypes: [BANNER, VIDEO, NATIVE], + + isBidRequestValid: function (opts) { + return Boolean(opts.bidId && opts.params && !isNaN(opts.params.placementId)) + }, + + buildRequests: function (validBidRequests) { + validBidRequests = validBidRequests || [] + let winTop = window + try { + window.top.location.toString() + winTop = window.top + } catch (e) { utils.logMessage(e) } + + const location = utils.getWindowLocation() + const placements = [] + + for (let i = 0; i < validBidRequests.length; i++) { + const p = validBidRequests[i] + + placements.push({ + placementId: p.params.placementId, + bidId: p.bidId, + traffic: p.params.traffic || BANNER + }) + } + + return { + method: 'POST', + url: URL, + data: { + deviceWidth: winTop.screen.width, + deviceHeight: winTop.screen.height, + language: (navigator && navigator.language) ? navigator.language : '', + secure: +(location.protocol === 'https:'), + host: location.hostname, + page: location.pathname, + placements: placements + } + } + }, + + interpretResponse: function (opts) { + const body = opts.body + const response = [] + + for (let i = 0; i < body.length; i++) { + const item = body[i] + if (isBidResponseValid(item)) { + delete item.mediaType + response.push(item) + } + } + + return response + }, + + getUserSyncs: function (syncOptions, serverResponses) { + return [{ type: 'image', url: URL_SYNC }] + } +} + +registerBidder(spec) + +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.title && bid.image && bid.impressionTrackers) + default: + return false + } +} diff --git a/modules/bidscubeBidAdapter.md b/modules/bidscubeBidAdapter.md new file mode 100644 index 00000000000..fa9e0432852 --- /dev/null +++ b/modules/bidscubeBidAdapter.md @@ -0,0 +1,27 @@ +# Overview + +``` +Module Name: BidsCube Bidder Adapter +Module Type: Bidder Adapter +Maintainer: publishers@bidscube.com +``` + +# Description + +Module that connects to BidsCube' demand sources + +# Test Parameters +``` + var adUnits = [{ + code: 'placementId_0', + sizes: [[300, 250]], + bids: [{ + bidder: 'bidscube', + params: { + placementId: 0, + traffic: 'banner' + } + }] + } + ]; +``` diff --git a/test/spec/modules/bidscubeBidAdapter_spec.js b/test/spec/modules/bidscubeBidAdapter_spec.js new file mode 100644 index 00000000000..65f31992852 --- /dev/null +++ b/test/spec/modules/bidscubeBidAdapter_spec.js @@ -0,0 +1,207 @@ +import { expect } from 'chai' +import { spec } from '../../../modules/bidscubeBidAdapter.js' +import { deepStrictEqual, notEqual, ok, strictEqual } from 'assert' + +describe('BidsCubeAdapter', () => { + const bid = { + bidId: '9ec5b177515ee2e5', + bidder: 'bidscube', + params: { + placementId: 0, + traffic: 'banner' + } + } + + describe('isBidRequestValid', () => { + it('Should return true if there are bidId, params and placementId parameters present', () => { + strictEqual(true, spec.isBidRequestValid(bid)) + }) + + it('Should return false if at least one of parameters is not present', () => { + const b = { ...bid } + delete b.params.placementId + strictEqual(false, spec.isBidRequestValid(b)) + }) + }) + + describe('buildRequests', () => { + const serverRequest = spec.buildRequests([bid]) + + it('Creates a ServerRequest object with method, URL and data', () => { + ok(serverRequest) + ok(serverRequest.method) + ok(serverRequest.url) + ok(serverRequest.data) + }) + + it('Returns POST method', () => { + strictEqual('POST', serverRequest.method) + }) + + it('Returns valid URL', () => { + strictEqual('https://supply.bidscube.com/?c=o&m=multi', serverRequest.url) + }) + + it('Returns valid data if array of bids is valid', () => { + const { data } = serverRequest + strictEqual('object', typeof data) + deepStrictEqual(['deviceWidth', 'deviceHeight', 'language', 'secure', 'host', 'page', 'placements'], Object.keys(data)) + strictEqual('number', typeof data.deviceWidth) + strictEqual('number', typeof data.deviceHeight) + strictEqual('string', typeof data.language) + strictEqual('string', typeof data.host) + strictEqual('string', typeof data.page) + notEqual(-1, [0, 1].indexOf(data.secure)) + + const placement = data.placements[0] + deepStrictEqual(['placementId', 'bidId', 'traffic'], Object.keys(placement)) + strictEqual(0, placement.placementId) + strictEqual('9ec5b177515ee2e5', placement.bidId) + strictEqual('banner', placement.traffic) + }) + + it('Returns empty data if no valid requests are passed', () => { + const { placements } = spec.buildRequests([]).data + + expect(spec.buildRequests([]).data.placements).to.be.an('array') + strictEqual(0, placements.length) + }) + }) + + describe('interpretResponse', () => { + const validData = [ + { + body: [{ + mediaType: 'banner', + width: 300, + height: 250, + cpm: 0.4, + ad: 'Test', + requestId: '9ec5b177515ee2e5', + ttl: 120, + creativeId: '2', + netRevenue: true, + currency: 'USD', + dealId: '1' + }] + }, + { + body: [{ + vastUrl: 'bidscube.com', + mediaType: 'video', + cpm: 0.5, + requestId: '9ec5b177515ee2e5', + ttl: 120, + creativeId: '2', + netRevenue: true, + currency: 'USD', + dealId: '1' + }] + }, + { + body: [{ + mediaType: 'native', + clickUrl: 'bidscube.com', + title: 'Test', + image: 'bidscube.com', + creativeId: '2', + impressionTrackers: ['bidscube.com'], + ttl: 120, + cpm: 0.4, + requestId: '9ec5b177515ee2e5', + netRevenue: true, + currency: 'USD', + }] + } + ] + + for (const obj of validData) { + const { mediaType } = obj.body[0] + + it(`Should interpret ${mediaType} response`, () => { + const response = spec.interpretResponse(obj) + + expect(response).to.be.an('array') + strictEqual(1, response.length) + + const copy = { ...obj.body[0] } + delete copy.mediaType + deepStrictEqual(copy, response[0]) + }) + } + + const invalidData = [ + { + body: [{ + width: 300, + cpm: 0.4, + ad: 'Test', + requestId: '9ec5b177515ee2e5', + ttl: 120, + creativeId: '2', + netRevenue: true, + currency: 'USD', + dealId: '1' + }] + }, + { + body: [{ + mediaType: 'video', + cpm: 0.5, + requestId: '9ec5b177515ee2e5', + ttl: 120, + creativeId: '2', + netRevenue: true, + currency: 'USD', + dealId: '1' + }] + }, + { + body: [{ + mediaType: 'native', + clickUrl: 'bidscube.com', + title: 'Test', + impressionTrackers: ['bidscube.com'], + ttl: 120, + requestId: '9ec5b177515ee2e5', + creativeId: '2', + netRevenue: true, + currency: 'USD', + }] + } + ] + + for (const obj of invalidData) { + const { mediaType } = obj.body[0] + + it(`Should return an empty array if invalid ${mediaType} response is passed `, () => { + const response = spec.interpretResponse(obj) + + expect(response).to.be.an('array') + strictEqual(0, response.length) + }) + } + + it('Should return an empty array if invalid response is passed', () => { + const response = spec.interpretResponse({ + body: [{ + ttl: 120, + creativeId: '2', + netRevenue: true, + currency: 'USD', + dealId: '1' + }] + }) + + expect(response).to.be.an('array') + strictEqual(0, response.length) + }) + }) + + describe('getUserSyncs', () => { + it('Returns valid URL and type', () => { + const expectedResult = [{ type: 'image', url: 'https://supply.bidscube.com/?c=o&m=cookie' }] + deepStrictEqual(expectedResult, spec.getUserSyncs()) + }) + }) +}) From 26260381a3e4a689f943b06798fee5341adb8e81 Mon Sep 17 00:00:00 2001 From: vlad Date: Tue, 11 May 2021 10:41:59 +0300 Subject: [PATCH 2/6] New Adapter: BidsCube --- modules/bidscubeBidAdapter.js | 2 +- modules/bidscubeBidAdapter.md | 6 +++++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/modules/bidscubeBidAdapter.js b/modules/bidscubeBidAdapter.js index 92eeb48d83b..c0a9a79e68c 100644 --- a/modules/bidscubeBidAdapter.js +++ b/modules/bidscubeBidAdapter.js @@ -11,7 +11,7 @@ export const spec = { supportedMediaTypes: [BANNER, VIDEO, NATIVE], isBidRequestValid: function (opts) { - return Boolean(opts.bidId && opts.params && !isNaN(opts.params.placementId)) + return Boolean(opts.bidId && opts.params && !isNaN(parseInt(opts.params.placementId))) }, buildRequests: function (validBidRequests) { diff --git a/modules/bidscubeBidAdapter.md b/modules/bidscubeBidAdapter.md index fa9e0432852..8e65000c25f 100644 --- a/modules/bidscubeBidAdapter.md +++ b/modules/bidscubeBidAdapter.md @@ -14,7 +14,11 @@ Module that connects to BidsCube' demand sources ``` var adUnits = [{ code: 'placementId_0', - sizes: [[300, 250]], + mediaTypes: { + banner: { + sizes: [[300, 250]] + } + }, bids: [{ bidder: 'bidscube', params: { From db38f61e5950ac180b8f3ad5e747e6774f455e4f Mon Sep 17 00:00:00 2001 From: vlad Date: Tue, 11 May 2021 11:27:59 +0300 Subject: [PATCH 3/6] New Adapter: BidsCube --- modules/bidscubeBidAdapter.js | 4 ++-- modules/bidscubeBidAdapter.md | 29 ++++++++++++++--------------- 2 files changed, 16 insertions(+), 17 deletions(-) diff --git a/modules/bidscubeBidAdapter.js b/modules/bidscubeBidAdapter.js index c0a9a79e68c..d3f27a5ac6d 100644 --- a/modules/bidscubeBidAdapter.js +++ b/modules/bidscubeBidAdapter.js @@ -31,7 +31,8 @@ export const spec = { placements.push({ placementId: p.params.placementId, bidId: p.bidId, - traffic: p.params.traffic || BANNER + traffic: p.params.traffic || BANNER, + allParams: JSON.stringify(p) }) } @@ -57,7 +58,6 @@ export const spec = { for (let i = 0; i < body.length; i++) { const item = body[i] if (isBidResponseValid(item)) { - delete item.mediaType response.push(item) } } diff --git a/modules/bidscubeBidAdapter.md b/modules/bidscubeBidAdapter.md index 8e65000c25f..5f3972726ec 100644 --- a/modules/bidscubeBidAdapter.md +++ b/modules/bidscubeBidAdapter.md @@ -13,19 +13,18 @@ Module that connects to BidsCube' demand sources # Test Parameters ``` var adUnits = [{ - code: 'placementId_0', - mediaTypes: { - banner: { - sizes: [[300, 250]] - } - }, - bids: [{ - bidder: 'bidscube', - params: { - placementId: 0, - traffic: 'banner' - } - }] - } - ]; + code: 'placementId_0', + mediaTypes: { + banner: { + sizes: [[300, 250]] + } + }, + bids: [{ + bidder: 'bidscube', + params: { + placementId: 0, + traffic: 'banner' + } + }] + }]; ``` From 40e07e9f2aceeea317f502d5a1b943176de49344 Mon Sep 17 00:00:00 2001 From: vlad Date: Tue, 11 May 2021 11:51:36 +0300 Subject: [PATCH 4/6] New Adapter: BidsCube --- modules/bidscubeBidAdapter.js | 1 + 1 file changed, 1 insertion(+) diff --git a/modules/bidscubeBidAdapter.js b/modules/bidscubeBidAdapter.js index d3f27a5ac6d..4834b7fdd7f 100644 --- a/modules/bidscubeBidAdapter.js +++ b/modules/bidscubeBidAdapter.js @@ -58,6 +58,7 @@ export const spec = { for (let i = 0; i < body.length; i++) { const item = body[i] if (isBidResponseValid(item)) { + delete item.mediaType response.push(item) } } From 16d5ccac442a4f2e2533de919f8c5e0de0381b0a Mon Sep 17 00:00:00 2001 From: vlad Date: Tue, 11 May 2021 12:28:43 +0300 Subject: [PATCH 5/6] New Adapter: BidsCube --- modules/bidscubeBidAdapter.js | 1 - test/spec/modules/bidscubeBidAdapter_spec.js | 7 ++++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/modules/bidscubeBidAdapter.js b/modules/bidscubeBidAdapter.js index 4834b7fdd7f..d3f27a5ac6d 100644 --- a/modules/bidscubeBidAdapter.js +++ b/modules/bidscubeBidAdapter.js @@ -58,7 +58,6 @@ export const spec = { for (let i = 0; i < body.length; i++) { const item = body[i] if (isBidResponseValid(item)) { - delete item.mediaType response.push(item) } } diff --git a/test/spec/modules/bidscubeBidAdapter_spec.js b/test/spec/modules/bidscubeBidAdapter_spec.js index 65f31992852..8098c720e46 100644 --- a/test/spec/modules/bidscubeBidAdapter_spec.js +++ b/test/spec/modules/bidscubeBidAdapter_spec.js @@ -8,7 +8,8 @@ describe('BidsCubeAdapter', () => { bidder: 'bidscube', params: { placementId: 0, - traffic: 'banner' + traffic: 'banner', + allParams: '{}' } } @@ -54,10 +55,11 @@ describe('BidsCubeAdapter', () => { notEqual(-1, [0, 1].indexOf(data.secure)) const placement = data.placements[0] - deepStrictEqual(['placementId', 'bidId', 'traffic'], Object.keys(placement)) + deepStrictEqual(['placementId', 'bidId', 'traffic', 'allParams'], Object.keys(placement)) strictEqual(0, placement.placementId) strictEqual('9ec5b177515ee2e5', placement.bidId) strictEqual('banner', placement.traffic) + strictEqual('{"bidId":"9ec5b177515ee2e5","bidder":"bidscube","params":{"placementId":0,"traffic":"banner","allParams":"{}"}}', placement.allParams) }) it('Returns empty data if no valid requests are passed', () => { @@ -125,7 +127,6 @@ describe('BidsCubeAdapter', () => { strictEqual(1, response.length) const copy = { ...obj.body[0] } - delete copy.mediaType deepStrictEqual(copy, response[0]) }) } From bd164fce07af6be8c41dbd91f28db01acec0a68b Mon Sep 17 00:00:00 2001 From: vlad Date: Fri, 4 Jun 2021 16:32:43 +0300 Subject: [PATCH 6/6] New Adapter: BidsCube --- test/spec/modules/bidscubeBidAdapter_spec.js | 22 ++++++++++++++++++-- 1 file changed, 20 insertions(+), 2 deletions(-) diff --git a/test/spec/modules/bidscubeBidAdapter_spec.js b/test/spec/modules/bidscubeBidAdapter_spec.js index 8098c720e46..26cf19fc310 100644 --- a/test/spec/modules/bidscubeBidAdapter_spec.js +++ b/test/spec/modules/bidscubeBidAdapter_spec.js @@ -84,7 +84,10 @@ describe('BidsCubeAdapter', () => { creativeId: '2', netRevenue: true, currency: 'USD', - dealId: '1' + dealId: '1', + meta: { + advertiserDomains: ['test.com'] + } }] }, { @@ -97,7 +100,10 @@ describe('BidsCubeAdapter', () => { creativeId: '2', netRevenue: true, currency: 'USD', - dealId: '1' + dealId: '1', + meta: { + advertiserDomains: ['test.com'] + } }] }, { @@ -113,6 +119,9 @@ describe('BidsCubeAdapter', () => { requestId: '9ec5b177515ee2e5', netRevenue: true, currency: 'USD', + meta: { + advertiserDomains: ['test.com'] + } }] } ] @@ -131,6 +140,15 @@ describe('BidsCubeAdapter', () => { }) } + for (const obj of validData) { + it(`Should interpret response has meta.advertiserDomains`, () => { + const response = spec.interpretResponse(obj) + + expect(response[0]['meta']['advertiserDomains']).to.be.an('array') + expect(response[0]['meta']['advertiserDomains'][0]).to.be.an('string') + }) + } + const invalidData = [ { body: [{