From ab73f52d42bac126216fbe2f11893b5b4b41d1de Mon Sep 17 00:00:00 2001 From: minoru katogi Date: Wed, 13 Mar 2019 11:56:52 +0100 Subject: [PATCH 1/4] Add Adman bid adapter --- modules/admanBidAdapter.js | 78 ++++++++ modules/admanBidAdapter.md | 35 ++++ test/spec/modules/admanBidAdapter_spec.js | 216 ++++++++++++++++++++++ 3 files changed, 329 insertions(+) create mode 100644 modules/admanBidAdapter.js create mode 100644 modules/admanBidAdapter.md create mode 100644 test/spec/modules/admanBidAdapter_spec.js diff --git a/modules/admanBidAdapter.js b/modules/admanBidAdapter.js new file mode 100644 index 00000000000..184dae299a6 --- /dev/null +++ b/modules/admanBidAdapter.js @@ -0,0 +1,78 @@ +import {registerBidder} from '../src/adapters/bidderFactory'; +import * as utils from '../src/utils'; + +const BIDDER_CODE = 'adman'; + +export const spec = { + code: BIDDER_CODE, + isBidRequestValid: function(bid) { + let isValid = false; + if (typeof bid.params !== 'undefined') { + let isValidId = _validateId(utils.getValue(bid.params, 'id')); + isValid = isValidId; + } + + if (!isValid) { + utils.logError('Adman id parameter is required. Bid aborted.'); + } + return isValid; + }, + buildRequests: function(validBidRequests, bidderRequest) { + const ENDPOINT_URL = '//bidtor.admanmedia.com/prebid'; + const bids = validBidRequests.map(buildRequestObject); + const payload = { + referrer: utils.getTopWindowUrl(), + bids, + deviceWidth: screen.width + }; + + if (bidderRequest && bidderRequest.gdprConsent) { + payload.gdpr = {}; + payload.gdpr.consent = bidderRequest.gdprConsent.consentString; + payload.gdpr.applies = bidderRequest.gdprConsent.gdprApplies; + } else { + payload.gdpr.consent = ''; + payload.gdpr = null; + } + + const payloadString = JSON.stringify(payload); + return { + method: 'POST', + url: ENDPOINT_URL, + data: payloadString, + }; + }, + interpretResponse: function(serverResponse) { + serverResponse = serverResponse.body; + return serverResponse.bids; + }, + getUserSyncs: function(syncOptions, responses, gdprApplies) { + if (syncOptions.iframeEnabled) { + return [{ + type: 'iframe', + url: '//cs.admanmedia.com/sync_tag/html' + }]; + } + } +}; + +function buildRequestObject(bid) { + const reqObj = {}; + + reqObj.params = {}; + reqObj.params.id = utils.getValue(bid.params, 'id'); + reqObj.params.bidId = bid.bidId; + reqObj.sizes = bid.sizes; + reqObj.bidId = utils.getBidIdParameter('bidId', bid); + reqObj.bidderRequestId = utils.getBidIdParameter('bidderRequestId', bid); + reqObj.adUnitCode = utils.getBidIdParameter('adUnitCode', bid); + reqObj.auctionId = utils.getBidIdParameter('auctionId', bid); + reqObj.transactionId = utils.getBidIdParameter('transactionId', bid); + return reqObj; +} + +function _validateId(id = '') { + return (id.length === 8); +} + +registerBidder(spec); diff --git a/modules/admanBidAdapter.md b/modules/admanBidAdapter.md new file mode 100644 index 00000000000..900c828ea5c --- /dev/null +++ b/modules/admanBidAdapter.md @@ -0,0 +1,35 @@ +# Overview + +**Module Name**: Adman Bidder Adapter +**Module Type**: Bidder Adapter +**Maintainer**: prebid@admanmedia.com + +# Description + +Use `adman` as bidder. + +`id` is required and must be 8 alphanumeric characters. + +## AdUnits configuration example +``` + var adUnits = [{ + code: 'test-div', + sizes: [[300, 250]], + bids: [{ + bidder: 'adman', + params: { + id: 1234asdf + } + }] + },{ + code: 'test-div, + sizes: [[600, 338]], + bids: [{ + bidder: 'adman', + params: { + id: asdf1234 + } + }] + }]; +``` + diff --git a/test/spec/modules/admanBidAdapter_spec.js b/test/spec/modules/admanBidAdapter_spec.js new file mode 100644 index 00000000000..f4b6e1375c0 --- /dev/null +++ b/test/spec/modules/admanBidAdapter_spec.js @@ -0,0 +1,216 @@ +import {expect} from 'chai'; +import {spec} from 'modules/admanBidAdapter'; +import {newBidder} from 'src/adapters/bidderFactory'; + +const ENDPOINT = '//bidtor.admanmedia.com/prebid'; +const BANNER = '"'; +const VAST = ''; +const USER_SYNC_IFRAME_URL = '//cs.admanmedia.com/sync_tag/html'; + +describe('admanBidAdapter', function() { + const adapter = newBidder(spec); + + describe('inherited functions', function() { + it('exists and is a function', function() { + expect(adapter.callBids).to.exist.and.to.be.a('function'); + }); + }); + + describe('isBidRequestValid', function() { + let bid = { + 'bidder': 'adman', + 'params': { + 'id': '1234asdf' + }, + 'adUnitCode': 'adunit-code', + 'sizes': [[300, 250], [300, 600]], + 'bidId': '30b31c1838de1e', + 'bidderRequestId': '22edbae2733bf6', + 'auctionId': '1d1a030790a475', + 'creativeId': 'er2ee' + }; + + it('should return true when required params found', function() { + expect(spec.isBidRequestValid(bid)).to.equal(true); + }); + + it('should return false when id is not valid (not string)', function() { + let bid = Object.assign({}, bid); + delete bid.params; + bid.params = { + 'id': 1234 + }; + + expect(spec.isBidRequestValid(bid)).to.equal(false); + }); + + it('should return false when required params are not passed', function() { + let bid = Object.assign({}, bid); + delete bid.params; + + bid.params = {}; + + expect(spec.isBidRequestValid(bid)).to.equal(false); + }); + }); + + describe('buildRequests', function() { + let bidRequests = [ + { + 'bidder': 'adman', + 'bidId': '51ef8751f9aead', + 'params': { + 'id': '1234asdf' + }, + 'adUnitCode': 'div-gpt-ad-1460505748561-0', + 'transactionId': 'd7b773de-ceaa-484d-89ca-d9f51b8d61ec', + 'sizes': [[320, 50], [300, 250], [300, 600]], + 'bidderRequestId': '418b37f85e772c', + 'auctionId': '18fd8b8b0bd757', + 'bidRequestsCount': 1 + } + ]; + + it('sends a valid bid request to ENDPOINT via POST', function() { + const request = spec.buildRequests(bidRequests, { + gdprConsent: { + consentString: 'BOZcQl_ObPFjWAeABAESCD-AAAAjx7_______9______9uz_Ov_v_f__33e8__9v_l_7_-___u_-33d4-_1vf99yfm1-7ftr3tp_87ues2_Xur__59__3z3_NohBgA', + gdprApplies: true + } + }); + + expect(request.url).to.equal(ENDPOINT); + expect(request.method).to.equal('POST'); + + const payload = JSON.parse(request.data); + console.log('>>payload', payload); + expect(payload.gdpr).to.exist; + + expect(payload.bids).to.exist.and.to.be.an('array').and.to.have.lengthOf(1); + expect(payload.referrer).to.exist; + + const bid = payload.bids[0]; + expect(bid).to.exist; + expect(bid.params).to.exist; + expect(bid.params.id).to.exist; + expect(bid.params.bidId).to.exist; + expect(bid.sizes).to.exist.and.to.be.an('array').and.to.have.lengthOf(3); + bid.sizes.forEach(size => { + expect(size).to.be.an('array').and.to.have.lengthOf(2); + expect(size[0]).to.be.a('number'); + expect(size[1]).to.be.a('number'); + }) + }); + + it('should send GDPR to endpoint and honor gdprApplies value', function() { + let consentString = 'bogusConsent'; + let bidderRequest = { + 'gdprConsent': { + 'consentString': consentString, + 'gdprApplies': true + } + }; + + const request = spec.buildRequests(bidRequests, bidderRequest); + const payload = JSON.parse(request.data); + expect(payload.gdpr).to.exist; + expect(payload.gdpr.consent).to.equal(consentString); + expect(payload.gdpr.applies).to.equal(true); + + let bidderRequest2 = { + 'gdprConsent': { + 'consentString': consentString, + 'gdprApplies': false + } + }; + + const request2 = spec.buildRequests(bidRequests, bidderRequest2); + const payload2 = JSON.parse(request2.data); + + expect(payload2.gdpr).to.exist; + expect(payload2.gdpr.consent).to.equal(consentString); + expect(payload2.gdpr.applies).to.equal(false); + }); + }); + + describe('interpretResponse', function() { + let bids = { + 'body': { + 'bids': [{ + 'ad': BANNER, + 'height': 250, + 'cpm': 0.5, + 'currency': 'USD', + 'netRevenue': true, + 'requestId': '3ede2a3fa0db94', + 'ttl': 3599, + 'width': 300, + 'creativeId': 'er2ee' + }, + { + 'vastXml': VAST, + 'cpm': 0.5, + 'currency': 'USD', + 'height': 250, + 'netRevenue': true, + 'requestId': '3ede2a3fa0db95', + 'ttl': 3599, + 'width': 300, + 'creativeId': 'er2ef' + }] + } + }; + + it('should get correct bid response', function() { + let expectedResponse = [{ + 'ad': BANNER, + 'cpm': 0.5, + 'creativeId': 'er2ee', + 'currency': 'USD', + 'height': 250, + 'netRevenue': true, + 'requestId': '3ede2a3fa0db94', + 'ttl': 3599, + 'width': 300, + }, + { + 'vastXml': VAST, + 'cpm': 0.5, + 'creativeId': 'er2ef', + 'currency': 'USD', + 'height': 250, + 'netRevenue': true, + 'requestId': '3ede2a3fa0db95', + 'ttl': 3599, + 'width': 300, + }]; + // los bids vienen formateados de server + let result = spec.interpretResponse(bids); + + expect(result[0]).to.deep.equal(expectedResponse[0]); + expect(result[1]).to.deep.equal(expectedResponse[1]); + // expect(Object.keys(result[1])).to.deep.equal(Object.keys(bids[1])); + }); + + it('handles nobid responses', function() { + let bids = { + 'body': { + 'bids': [] + } + }; + + let result = spec.interpretResponse(bids); + expect(result.length).to.equal(0); + }); + }); + describe('getUserSyncs', () => { + it('should get correct user sync iframe url', function() { + expect(spec.getUserSyncs({ + iframeEnabled: true + }, [{}])).to.deep.equal([{ + type: 'iframe', + url: USER_SYNC_IFRAME_URL + }]); + }); + }); +}); From 87ac067fc11970ab0e6fe6c0f38217236aaaa2e5 Mon Sep 17 00:00:00 2001 From: minoru katogi Date: Fri, 15 Mar 2019 12:43:23 +0100 Subject: [PATCH 2/4] Add supportedMediaTypes property --- modules/admanBidAdapter.js | 1 + 1 file changed, 1 insertion(+) diff --git a/modules/admanBidAdapter.js b/modules/admanBidAdapter.js index 184dae299a6..ea9c946638d 100644 --- a/modules/admanBidAdapter.js +++ b/modules/admanBidAdapter.js @@ -5,6 +5,7 @@ const BIDDER_CODE = 'adman'; export const spec = { code: BIDDER_CODE, + supportedMediaTypes: ['video', 'banner'], isBidRequestValid: function(bid) { let isValid = false; if (typeof bid.params !== 'undefined') { From 645328aed4eae98b648eaecae153d5e66a5a84a7 Mon Sep 17 00:00:00 2001 From: mkatogi Date: Tue, 19 Mar 2019 15:14:45 +0100 Subject: [PATCH 3/4] Update ADman Media bidder adapter --- modules/admanBidAdapter.js | 50 +++++++++++++++++++------------------- 1 file changed, 25 insertions(+), 25 deletions(-) diff --git a/modules/admanBidAdapter.js b/modules/admanBidAdapter.js index ea9c946638d..2fd8c2e96f0 100644 --- a/modules/admanBidAdapter.js +++ b/modules/admanBidAdapter.js @@ -7,12 +7,7 @@ export const spec = { code: BIDDER_CODE, supportedMediaTypes: ['video', 'banner'], isBidRequestValid: function(bid) { - let isValid = false; - if (typeof bid.params !== 'undefined') { - let isValidId = _validateId(utils.getValue(bid.params, 'id')); - isValid = isValidId; - } - + const isValid = _validateId(utils.deepAccess(bid, 'params.id')); if (!isValid) { utils.logError('Adman id parameter is required. Bid aborted.'); } @@ -28,12 +23,14 @@ export const spec = { }; if (bidderRequest && bidderRequest.gdprConsent) { - payload.gdpr = {}; - payload.gdpr.consent = bidderRequest.gdprConsent.consentString; - payload.gdpr.applies = bidderRequest.gdprConsent.gdprApplies; + payload.gdpr = { + consent: bidderRequest.gdprConsent.consentString, + applies: bidderRequest.gdprConsent.gdprApplies + }; } else { - payload.gdpr.consent = ''; - payload.gdpr = null; + payload.gdpr = { + consent: '' + } } const payloadString = JSON.stringify(payload); @@ -45,9 +42,12 @@ export const spec = { }, interpretResponse: function(serverResponse) { serverResponse = serverResponse.body; - return serverResponse.bids; + if (serverResponse && typeof serverResponse.bids === 'object') { + return serverResponse.bids; + } + return []; }, - getUserSyncs: function(syncOptions, responses, gdprApplies) { + getUserSyncs: function(syncOptions) { if (syncOptions.iframeEnabled) { return [{ type: 'iframe', @@ -58,18 +58,18 @@ export const spec = { }; function buildRequestObject(bid) { - const reqObj = {}; - - reqObj.params = {}; - reqObj.params.id = utils.getValue(bid.params, 'id'); - reqObj.params.bidId = bid.bidId; - reqObj.sizes = bid.sizes; - reqObj.bidId = utils.getBidIdParameter('bidId', bid); - reqObj.bidderRequestId = utils.getBidIdParameter('bidderRequestId', bid); - reqObj.adUnitCode = utils.getBidIdParameter('adUnitCode', bid); - reqObj.auctionId = utils.getBidIdParameter('auctionId', bid); - reqObj.transactionId = utils.getBidIdParameter('transactionId', bid); - return reqObj; + return { + params: { + id: utils.getValue(bid.params, 'id'), + bidId: bid.bidId + }, + sizes: bid.sizes, + bidId: utils.getBidIdParameter('bidId', bid), + bidderRequestId: utils.getBidIdParameter('bidderRequestId', bid), + adUnitCode: utils.getBidIdParameter('adUnitCode', bid), + auctionId: utils.getBidIdParameter('auctionId', bid), + transactionId: utils.getBidIdParameter('transactionId', bid) + }; } function _validateId(id = '') { From 3ea8a3cb61f3d44277c8ec2780611338d5229230 Mon Sep 17 00:00:00 2001 From: mkatogi Date: Wed, 20 Mar 2019 07:56:05 +0100 Subject: [PATCH 4/4] Remove console.log --- test/spec/modules/admanBidAdapter_spec.js | 1 - 1 file changed, 1 deletion(-) diff --git a/test/spec/modules/admanBidAdapter_spec.js b/test/spec/modules/admanBidAdapter_spec.js index f4b6e1375c0..2af040103cc 100644 --- a/test/spec/modules/admanBidAdapter_spec.js +++ b/test/spec/modules/admanBidAdapter_spec.js @@ -83,7 +83,6 @@ describe('admanBidAdapter', function() { expect(request.method).to.equal('POST'); const payload = JSON.parse(request.data); - console.log('>>payload', payload); expect(payload.gdpr).to.exist; expect(payload.bids).to.exist.and.to.be.an('array').and.to.have.lengthOf(1);