From cdd2ebceaacd8f99d42303d8b1fd75b65e92aaf7 Mon Sep 17 00:00:00 2001 From: joan Date: Tue, 14 Nov 2017 17:08:56 +0800 Subject: [PATCH 1/5] Add a new bid adapter and test specs for bridgewell --- modules/bridgewellBidAdapter.js | 101 ++++++++++ modules/bridgewellBidAdapter.md | 38 ++++ .../spec/modules/bridgewellBidAdapter_spec.js | 174 ++++++++++++++++++ 3 files changed, 313 insertions(+) create mode 100644 modules/bridgewellBidAdapter.js create mode 100644 modules/bridgewellBidAdapter.md create mode 100644 test/spec/modules/bridgewellBidAdapter_spec.js diff --git a/modules/bridgewellBidAdapter.js b/modules/bridgewellBidAdapter.js new file mode 100644 index 00000000000..aa3fc1bb3de --- /dev/null +++ b/modules/bridgewellBidAdapter.js @@ -0,0 +1,101 @@ +import * as utils from 'src/utils'; +import {registerBidder} from 'src/adapters/bidderFactory'; + +const BIDDER_CODE = 'bridgewell'; +const REQUEST_ENDPOINT = '//rec.scupio.com/recweb/prebid.aspx'; + +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) { + return bid && bid.params && !!bid.params.ChannelID; + }, + + /** + * Make a server request from the list of BidRequests. + * + * @param {BidRequest[]} validBidRequests - an array of bids + * @return ServerRequest Info describing the request to the server. + */ + buildRequests: function(validBidRequests) { + var channelIDs = []; + + utils._each(validBidRequests, function(bid) { + channelIDs.push(bid.params.ChannelID); + }); + + return { + method: 'GET', + url: REQUEST_ENDPOINT, + data: { + 'ChannelID': channelIDs.join(',') + }, + validBidRequests: validBidRequests + }; + }, + + /** + * Unpack the response from the server into a list of bids. + * + * @param {*} serverResponse A successful response from the server. + * @param {*} bidRequest + * @return {Bid[]} An array of bids which were nested inside the server. + */ + interpretResponse: function(serverResponse, bidRequest) { + var bidResponses = []; + + // map responses to requests + utils._each(bidRequest.validBidRequests, function(req) { + var bidResponse = {}; + var matchedResponse = serverResponse.body.find(function(res) { + return !res.consumed && req.sizes.find(function(size) { + return res.width === size[0] && res.height === size[1]; + }); + }); + + if (matchedResponse) { + matchedResponse.consumed = true; + + // check required parameters + if (typeof matchedResponse.cpm !== 'number') { + return; + } else if (typeof matchedResponse.width !== 'number' || typeof matchedResponse.height !== 'number') { + return; + } else if (typeof matchedResponse.ad !== 'string') { + return; + } else if (typeof matchedResponse.net_revenue === 'undefined') { + return; + } else if (typeof matchedResponse.currency !== 'string') { + return; + } + + bidResponse.bidderCode = spec.code; + bidResponse.requestId = req.bidId; + bidResponse.cpm = matchedResponse.cpm; + bidResponse.width = matchedResponse.width; + bidResponse.height = matchedResponse.height; + bidResponse.ad = matchedResponse.ad; + bidResponse.ttl = matchedResponse.ttl; + bidResponse.creativeId = matchedResponse.id; + bidResponse.netRevenue = matchedResponse.net_revenue === 'true'; + bidResponse.currency = matchedResponse.currency; + + bidResponses.push(bidResponse); + } + }); + + return bidResponses; + }, + + getUserSyncs: function(syncOptions) { + // currently not needed + } +}; + +registerBidder(spec); diff --git a/modules/bridgewellBidAdapter.md b/modules/bridgewellBidAdapter.md new file mode 100644 index 00000000000..b11ce21b095 --- /dev/null +++ b/modules/bridgewellBidAdapter.md @@ -0,0 +1,38 @@ +# Overview + +Module Name: Bridgewell Bidder Adapter +Module Type: Bidder Adapter +Maintainer: kuchunchou@bridgewell.com + +# Description + +Module that connects to Bridgewell demand source to fetch bids. + +# Test Parameters +``` + var adUnits = [ + { + code: 'test-div', + sizes: [[300, 250]], + bids: [ + { + bidder: 'bridgewell', + params: { + ChannelID: 'CgUxMjMzOBIBNiIFcGVubnkqCQisAhD6ARoBOQ', + } + } + ] + },{ + code: 'test-div', + sizes: [[728, 90]], + bids: [ + { + bidder: 'bridgewell', + params: { + ChannelID: 'CgUxMjMzOBIBNiIGcGVubnkzKggI2AUQWhoBOQ', + } + } + ] + } + ]; +``` diff --git a/test/spec/modules/bridgewellBidAdapter_spec.js b/test/spec/modules/bridgewellBidAdapter_spec.js new file mode 100644 index 00000000000..470f297222e --- /dev/null +++ b/test/spec/modules/bridgewellBidAdapter_spec.js @@ -0,0 +1,174 @@ +import { expect } from 'chai'; +import { spec } from 'modules/bridgewellBidAdapter'; +import { newBidder } from 'src/adapters/bidderFactory'; + +describe('bridgewellBidAdapter', function () { + let bidRequests = [ + { + 'bidder': 'bridgewell', + 'params': { + 'ChannelID': 'CLJgEAYYvxUiBXBlbm55KgkIrAIQ-gEaATk' + }, + 'adUnitCode': 'adunit-code-1', + 'sizes': [[300, 250], [300, 600]], + 'bidId': '30b31c1838de1e', + 'bidderRequestId': '22edbae2733bf6', + 'auctionId': '1d1a030790a475', + }, + { + 'bidder': 'bridgewell', + 'params': { + 'ChannelID': 'CgUxMjMzOBIBNiIGcGVubnkzKggI2AUQWhoBOQ' + }, + 'adUnitCode': 'adunit-code-2', + 'sizes': [[728, 90]], + 'bidId': '3150ccb55da321', + 'bidderRequestId': '22edbae2733bf6', + 'auctionId': '1d1a030790a475', + }, + { + 'bidder': 'bridgewell', + 'params': { + 'ChannelID': 'CgUxMjMzOBIBNiIFcGVubnkqCQisAhD6ARoBOQ' + }, + 'adUnitCode': 'adunit-code-1', + 'sizes': [[300, 250]], + 'bidId': '42dbe3a7168a6a', + 'bidderRequestId': '22edbae2733bf6', + 'auctionId': '1d1a030790a475', + } + ]; + const adapter = newBidder(spec); + + describe('inherited functions', () => { + it('exists and is a function', () => { + expect(adapter.callBids).to.exist.and.to.be.a('function'); + }); + }); + + describe('isBidRequestValid', () => { + let bid = { + 'bidder': 'bridgewell', + 'params': { + 'ChannelID': 'CLJgEAYYvxUiBXBlbm55KgkIrAIQ-gEaATk' + }, + 'adUnitCode': 'adunit-code', + 'sizes': [[300, 250], [300, 600]], + 'bidId': '30b31c1838de1e', + 'bidderRequestId': '22edbae2733bf6', + 'auctionId': '1d1a030790a475', + }; + + 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 = { + 'ChannelID': 0 + }; + expect(spec.isBidRequestValid(bid)).to.equal(false); + }); + }); + + describe('buildRequests', () => { + it('should attach valid params to the tag', () => { + const request = spec.buildRequests([bidRequests[0]]); + const payload = request.data; + expect(payload).to.be.an('object'); + expect(payload).to.have.property('ChannelID').that.is.a('string'); + }); + + it('should attach validBidRequests to the tag', () => { + const request = spec.buildRequests(bidRequests); + const validBidRequests = request.validBidRequests; + expect(validBidRequests).to.deep.equal(bidRequests); + }); + + it('should attach valid params to the tag if multiple ChannelIDs are presented', () => { + const request = spec.buildRequests(bidRequests); + const payload = request.data; + expect(payload).to.be.an('object'); + expect(payload).to.have.property('ChannelID').that.is.a('string'); + expect(payload.ChannelID.split(',')).to.have.lengthOf(bidRequests.length); + }); + }); + + describe('interpretResponse', () => { + const request = spec.buildRequests(bidRequests); + const serverResponses = [{ + 'id': 'e5b10774-32bf-4931-85ee-05095e8cff21', + 'bidder_code': 'bridgewell', + 'cpm': 5.0, + 'width': 300, + 'height': 250, + 'ad': '
test 300x250
', + 'ttl': 360, + 'net_revenue': 'true', + 'currency': 'NTD' + }, { + 'id': '0e4048d3-5c74-4380-a21a-00ba35629f7d', + 'bidder_code': 'bridgewell', + 'cpm': 5.0, + 'width': 728, + 'height': 90, + 'ad': '
test 728x90
', + 'ttl': 360, + 'net_revenue': 'true', + 'currency': 'NTD' + }, { + 'id': '8f12c646-3b87-4326-a837-c2a76999f168', + 'bidder_code': 'bridgewell', + 'cpm': 5.0, + 'width': 300, + 'height': 250, + 'ad': '
test 300x250
', + 'ttl': 360, + 'net_revenue': 'true', + 'currency': 'NTD' + }]; + + it('should give up bid if server response is undefiend', () => { + const result = spec.interpretResponse(undefined, request); + expect(result).to.deep.equal([]); + }); + + it('should give up bid if cpm is missing', () => { + let target = Object.assign({}, serverResponses[0]); + delete target.cpm; + const result = spec.interpretResponse([target], request); + expect(result).to.deep.equal([]); + }); + + it('should give up bid if width or height is missing', () => { + let target = Object.assign({}, serverResponses[0]); + delete target.height; + delete target.width; + const result = spec.interpretResponse([target], request); + expect(result).to.deep.equal([]); + }); + + it('should give up bid if ad is missing', () => { + let target = Object.assign({}, serverResponses[0]); + delete target.ad; + const result = spec.interpretResponse([target], request); + expect(result).to.deep.equal([]); + }); + + it('should give up bid if revenue mode is missing', () => { + let target = Object.assign({}, serverResponses[0]); + delete target.net_revenue; + const result = spec.interpretResponse([target], request); + expect(result).to.deep.equal([]); + }); + + it('should give up bid if currency is missing', () => { + let target = Object.assign({}, serverResponses[0]); + delete target.currency; + const result = spec.interpretResponse([target], request); + expect(result).to.deep.equal([]); + }); + }); +}); From 191d442ffd1877f14bfb526143d32c32ef9f90ab Mon Sep 17 00:00:00 2001 From: joan Date: Fri, 17 Nov 2017 09:09:08 +0800 Subject: [PATCH 2/5] remove getUserSyncs(optional) --- modules/bridgewellBidAdapter.js | 5 ----- 1 file changed, 5 deletions(-) diff --git a/modules/bridgewellBidAdapter.js b/modules/bridgewellBidAdapter.js index aa3fc1bb3de..312ac09174f 100644 --- a/modules/bridgewellBidAdapter.js +++ b/modules/bridgewellBidAdapter.js @@ -75,7 +75,6 @@ export const spec = { return; } - bidResponse.bidderCode = spec.code; bidResponse.requestId = req.bidId; bidResponse.cpm = matchedResponse.cpm; bidResponse.width = matchedResponse.width; @@ -91,10 +90,6 @@ export const spec = { }); return bidResponses; - }, - - getUserSyncs: function(syncOptions) { - // currently not needed } }; From 8bbdc5bb6462a60dc338505fc770bad8978e3b87 Mon Sep 17 00:00:00 2001 From: joan Date: Fri, 17 Nov 2017 09:13:49 +0800 Subject: [PATCH 3/5] change var to let or const --- modules/bridgewellBidAdapter.js | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/modules/bridgewellBidAdapter.js b/modules/bridgewellBidAdapter.js index 312ac09174f..4bfe778cef2 100644 --- a/modules/bridgewellBidAdapter.js +++ b/modules/bridgewellBidAdapter.js @@ -24,7 +24,7 @@ export const spec = { * @return ServerRequest Info describing the request to the server. */ buildRequests: function(validBidRequests) { - var channelIDs = []; + const channelIDs = []; utils._each(validBidRequests, function(bid) { channelIDs.push(bid.params.ChannelID); @@ -48,12 +48,12 @@ export const spec = { * @return {Bid[]} An array of bids which were nested inside the server. */ interpretResponse: function(serverResponse, bidRequest) { - var bidResponses = []; + const bidResponses = []; // map responses to requests utils._each(bidRequest.validBidRequests, function(req) { - var bidResponse = {}; - var matchedResponse = serverResponse.body.find(function(res) { + const bidResponse = {}; + let matchedResponse = serverResponse.body.find(function(res) { return !res.consumed && req.sizes.find(function(size) { return res.width === size[0] && res.height === size[1]; }); From f1ee688e74684d367ff551c16da9a152fed3aea0 Mon Sep 17 00:00:00 2001 From: joan Date: Fri, 17 Nov 2017 14:33:21 +0800 Subject: [PATCH 4/5] fix a bug in adapter test spec, and add a test for required parameters in bid responses --- modules/bridgewellBidAdapter.js | 7 +++++- .../spec/modules/bridgewellBidAdapter_spec.js | 24 ++++++++++++++----- 2 files changed, 24 insertions(+), 7 deletions(-) diff --git a/modules/bridgewellBidAdapter.js b/modules/bridgewellBidAdapter.js index 4bfe778cef2..3ae08c8764f 100644 --- a/modules/bridgewellBidAdapter.js +++ b/modules/bridgewellBidAdapter.js @@ -53,8 +53,13 @@ export const spec = { // map responses to requests utils._each(bidRequest.validBidRequests, function(req) { const bidResponse = {}; + + if (!serverResponse.body) { + return; + } + let matchedResponse = serverResponse.body.find(function(res) { - return !res.consumed && req.sizes.find(function(size) { + return !!res && !res.consumed && req.sizes.find(function(size) { return res.width === size[0] && res.height === size[1]; }); }); diff --git a/test/spec/modules/bridgewellBidAdapter_spec.js b/test/spec/modules/bridgewellBidAdapter_spec.js index 470f297222e..578b0f50cc0 100644 --- a/test/spec/modules/bridgewellBidAdapter_spec.js +++ b/test/spec/modules/bridgewellBidAdapter_spec.js @@ -1,6 +1,7 @@ import { expect } from 'chai'; import { spec } from 'modules/bridgewellBidAdapter'; import { newBidder } from 'src/adapters/bidderFactory'; +import * as utils from 'src/utils'; describe('bridgewellBidAdapter', function () { let bidRequests = [ @@ -130,15 +131,26 @@ describe('bridgewellBidAdapter', function () { 'currency': 'NTD' }]; + it('should return all required parameters', () => { + const result = spec.interpretResponse({'body': serverResponses}, request); + result.every(res => expect(res.cpm).to.be.a('number')) + result.every(res => expect(res.width).to.be.a('number')) + result.every(res => expect(res.height).to.be.a('number')) + result.every(res => expect(res.ttl).to.be.a('number')) + result.every(res => expect(res.netRevenue).to.be.a('boolean')) + result.every(res => expect(res.currency).to.be.a('string')) + result.every(res => expect(res.ad).to.be.a('string')) + }); + it('should give up bid if server response is undefiend', () => { - const result = spec.interpretResponse(undefined, request); + const result = spec.interpretResponse({'body': undefined}, request); expect(result).to.deep.equal([]); }); it('should give up bid if cpm is missing', () => { let target = Object.assign({}, serverResponses[0]); delete target.cpm; - const result = spec.interpretResponse([target], request); + const result = spec.interpretResponse({'body': [target]}, request); expect(result).to.deep.equal([]); }); @@ -146,28 +158,28 @@ describe('bridgewellBidAdapter', function () { let target = Object.assign({}, serverResponses[0]); delete target.height; delete target.width; - const result = spec.interpretResponse([target], request); + const result = spec.interpretResponse({'body': [target]}, request); expect(result).to.deep.equal([]); }); it('should give up bid if ad is missing', () => { let target = Object.assign({}, serverResponses[0]); delete target.ad; - const result = spec.interpretResponse([target], request); + const result = spec.interpretResponse({'body': [target]}, request); expect(result).to.deep.equal([]); }); it('should give up bid if revenue mode is missing', () => { let target = Object.assign({}, serverResponses[0]); delete target.net_revenue; - const result = spec.interpretResponse([target], request); + const result = spec.interpretResponse({'body': [target]}, request); expect(result).to.deep.equal([]); }); it('should give up bid if currency is missing', () => { let target = Object.assign({}, serverResponses[0]); delete target.currency; - const result = spec.interpretResponse([target], request); + const result = spec.interpretResponse({'body': [target]}, request); expect(result).to.deep.equal([]); }); }); From 75fa6b07238e472d37eefd141c998715e04a1bd9 Mon Sep 17 00:00:00 2001 From: joan Date: Fri, 17 Nov 2017 14:56:08 +0800 Subject: [PATCH 5/5] add semicolons to match styling --- test/spec/modules/bridgewellBidAdapter_spec.js | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/test/spec/modules/bridgewellBidAdapter_spec.js b/test/spec/modules/bridgewellBidAdapter_spec.js index 578b0f50cc0..7670d992d0d 100644 --- a/test/spec/modules/bridgewellBidAdapter_spec.js +++ b/test/spec/modules/bridgewellBidAdapter_spec.js @@ -133,13 +133,13 @@ describe('bridgewellBidAdapter', function () { it('should return all required parameters', () => { const result = spec.interpretResponse({'body': serverResponses}, request); - result.every(res => expect(res.cpm).to.be.a('number')) - result.every(res => expect(res.width).to.be.a('number')) - result.every(res => expect(res.height).to.be.a('number')) - result.every(res => expect(res.ttl).to.be.a('number')) - result.every(res => expect(res.netRevenue).to.be.a('boolean')) - result.every(res => expect(res.currency).to.be.a('string')) - result.every(res => expect(res.ad).to.be.a('string')) + result.every(res => expect(res.cpm).to.be.a('number')); + result.every(res => expect(res.width).to.be.a('number')); + result.every(res => expect(res.height).to.be.a('number')); + result.every(res => expect(res.ttl).to.be.a('number')); + result.every(res => expect(res.netRevenue).to.be.a('boolean')); + result.every(res => expect(res.currency).to.be.a('string')); + result.every(res => expect(res.ad).to.be.a('string')); }); it('should give up bid if server response is undefiend', () => {