diff --git a/modules/7xbidBidAdapter.js b/modules/7xbidBidAdapter.js new file mode 100644 index 00000000000..b925912a399 --- /dev/null +++ b/modules/7xbidBidAdapter.js @@ -0,0 +1,159 @@ +import * as utils from '../src/utils.js'; +import { registerBidder } from '../src/adapters/bidderFactory.js'; + +const BIDDER_CODE = '7xbid'; +const BIDDER_ALIAS = '7xb'; +const ENDPOINT_BANNER = '//bidder.7xbid.com/api/v1/prebid/banner'; +const ENDPOINT_NATIVE = '//bidder.7xbid.com/api/v1/prebid/native'; +const COOKIE_SYNC_URL = '//bidder.7xbid.com/api/v1/cookie/gen'; +const SUPPORTED_MEDIA_TYPES = ['banner', 'native']; +const SUPPORTED_CURRENCIES = ['USD', 'JPY']; +const DEFAULT_CURRENCY = 'JPY'; +const NET_REVENUE = true; + +/** + * updated to support prebid 3.0 - remove utils.getTopWindowUrl() + */ + +const _encodeURIComponent = function(a) { + let b = window.encodeURIComponent(a); + b = b.replace(/'/g, '%27'); + return b; +} + +export const _getUrlVars = function(url) { + var hash; + var myJson = {}; + var hashes = url.slice(url.indexOf('?') + 1).split('&'); + for (var i = 0; i < hashes.length; i++) { + hash = hashes[i].split('='); + myJson[hash[0]] = hash[1]; + } + return myJson; +} + +export const spec = { + code: BIDDER_CODE, + aliases: [BIDDER_ALIAS], // short code + supportedMediaTypes: SUPPORTED_MEDIA_TYPES, + isBidRequestValid: function(bid) { + if (!(bid.params.placementId)) { + return false; + } + + if (bid.params.hasOwnProperty('currency') && + SUPPORTED_CURRENCIES.indexOf(bid.params.currency) === -1) { + utils.logInfo('Invalid currency type, we support only JPY and USD!') + return false; + } + + return true; + }, + /** + * Make a server request from the list of BidRequests. + * + * @param {validBidRequests[]} - an array of bids + * @return ServerRequest Info describing the request to the server. + */ + buildRequests: function(validBidRequests, bidderRequest) { + let serverRequests = []; + var refererInfo; + if (bidderRequest && bidderRequest.refererInfo) { + refererInfo = bidderRequest.refererInfo; + } + validBidRequests.forEach((bid, i) => { + let endpoint = ENDPOINT_BANNER + let data = { + 'placementid': bid.params.placementId, + 'cur': bid.params.hasOwnProperty('currency') ? bid.params.currency : DEFAULT_CURRENCY, + 'ua': navigator.userAgent, + 'loc': utils.deepAccess(bidderRequest, 'refererInfo.referer'), + 'topframe': (window.parent === window.self) ? 1 : 0, + 'sw': screen && screen.width, + 'sh': screen && screen.height, + 'cb': Math.floor(Math.random() * 99999999999), + 'tpaf': 1, + 'cks': 1, + 'requestid': bid.bidId + }; + + if (bid.hasOwnProperty('nativeParams')) { + endpoint = ENDPOINT_NATIVE + data.tkf = 1 // return url tracker + data.ad_track = '1' + data.apiv = '1.1.0' + } + + if (refererInfo && refererInfo.referer) { + data.referer = refererInfo.referer; + } else { + data.referer = ''; + } + + serverRequests.push({ + method: 'GET', + url: endpoint, + data: utils.parseQueryStringParameters(data) + }) + }) + + return serverRequests; + }, + interpretResponse: function(serverResponse, request) { + const data = _getUrlVars(request.data) + const successBid = serverResponse.body || {}; + let bidResponses = []; + if (successBid.hasOwnProperty(data.placementid)) { + let bid = successBid[data.placementid] + let bidResponse = { + requestId: bid.requestid, + cpm: bid.price, + creativeId: bid.creativeId, + currency: bid.cur, + netRevenue: NET_REVENUE, + ttl: 700 + }; + + if (bid.hasOwnProperty('title')) { // it is native ad response + bidResponse.mediaType = 'native' + bidResponse.native = { + title: bid.title, + body: bid.description, + cta: bid.cta, + sponsoredBy: bid.advertiser, + clickUrl: _encodeURIComponent(bid.landingURL), + impressionTrackers: bid.trackings, + } + if (bid.screenshots) { + bidResponse.native.image = { + url: bid.screenshots.url, + height: bid.screenshots.height, + width: bid.screenshots.width, + } + } + if (bid.icon) { + bidResponse.native.icon = { + url: bid.icon.url, + height: bid.icon.height, + width: bid.icon.width, + } + } + } else { + bidResponse.ad = bid.adm + bidResponse.width = bid.width + bidResponse.height = bid.height + } + + bidResponses.push(bidResponse); + } + + return bidResponses; + }, + getUserSyncs: function(syncOptions, serverResponses) { + return [{ + type: 'image', + url: COOKIE_SYNC_URL + }]; + } +} +registerBidder(spec); diff --git a/modules/7xbidBidAdapter.md b/modules/7xbidBidAdapter.md index 13a448da992..692428332f0 100644 --- a/modules/7xbidBidAdapter.md +++ b/modules/7xbidBidAdapter.md @@ -24,7 +24,6 @@ Module that connects to 7xbid's demand sources params: { placementId: 1425292, currency: 'USD' - } } ] diff --git a/test/spec/modules/7xbidBidAdapter_spec.js b/test/spec/modules/7xbidBidAdapter_spec.js new file mode 100644 index 00000000000..4f32e96ce93 --- /dev/null +++ b/test/spec/modules/7xbidBidAdapter_spec.js @@ -0,0 +1,161 @@ +import {expect} from 'chai'; +import {spec, _getUrlVars} from 'modules/7xbidBidAdapter'; +import * as utils from 'src/utils'; +import {config} from 'src/config'; + +const BASE_URI = '//bidder.7xbid.com/api/v1/prebid/banner' +const NATIVE_BASE_URI = '//bidder.7xbid.com/api/v1/prebid/native' + +describe('7xbid adapter', function() { + let bidRequests; + let nativeBidRequests; + + beforeEach(function() { + bidRequests = [ + { + bidder: '7xbid', + params: { + placementId: 1425292, + currency: 'USD' + } + } + ] + + nativeBidRequests = [ + { + bidder: '7xbid', + params: { + placementId: 1429695, + currency: 'USD' + }, + nativeParams: { + title: { + required: true, + len: 80 + }, + image: { + required: true, + sizes: [150, 50] + }, + sponsoredBy: { + required: true + } + } + } + ] + }) + describe('isBidRequestValid', function () { + it('valid bid case', function () { + let validBid = { + bidder: '7xbid', + params: { + placementId: 1425292, + currency: 'USD' + } + } + let isValid = spec.isBidRequestValid(validBid); + expect(isValid).to.equal(true); + }); + + it('invalid bid case: placementId is not passed', function() { + let validBid = { + bidder: '7xbid', + params: { + } + } + let isValid = spec.isBidRequestValid(validBid); + expect(isValid).to.equal(false); + }) + + it('invalid bid case: currency is not support', function() { + let validBid = { + bidder: '7xbid', + params: { + placementId: 1108295, + currency: 'AUD' + } + } + let isValid = spec.isBidRequestValid(validBid); + expect(isValid).to.equal(false); + }) + }) + + describe('buildRequests', function () { + it('sends bid request to ENDPOINT via GET', function () { + const request = spec.buildRequests(bidRequests)[0]; + expect(request.url).to.equal(BASE_URI); + expect(request.method).to.equal('GET'); + }); + + it('sends native bid request to ENDPOINT via GET', function () { + const request = spec.buildRequests(nativeBidRequests)[0]; + expect(request.url).to.equal(NATIVE_BASE_URI); + expect(request.method).to.equal('GET'); + }); + + it('buildRequests function should not modify original bidRequests object', function () { + let originalBidRequests = utils.deepClone(bidRequests); + let request = spec.buildRequests(bidRequests); + expect(bidRequests).to.deep.equal(originalBidRequests); + }); + + it('buildRequests function should not modify original nativeBidRequests object', function () { + let originalBidRequests = utils.deepClone(nativeBidRequests); + let request = spec.buildRequests(nativeBidRequests); + expect(nativeBidRequests).to.deep.equal(originalBidRequests); + }); + + it('Request params check', function() { + let request = spec.buildRequests(bidRequests)[0]; + const data = _getUrlVars(request.data) + expect(parseInt(data.placementid)).to.exist.and.to.equal(bidRequests[0].params.placementId); + expect(data.cur).to.exist.and.to.equal(bidRequests[0].params.currency); + }) + + it('Native request params check', function() { + let request = spec.buildRequests(nativeBidRequests)[0]; + const data = _getUrlVars(request.data) + expect(parseInt(data.placementid)).to.exist.and.to.equal(nativeBidRequests[0].params.placementId); + expect(data.cur).to.exist.and.to.equal(nativeBidRequests[0].params.currency); + }) + }) + + describe('interpretResponse', function () { + let response = { + 1425292: + { + 'creativeId': '', + 'cur': 'USD', + 'price': 0.0920, + 'width': 300, + 'height': 250, + 'requestid': '2e42361a6172bf', + 'adm': '' + } + } + + it('should get correct bid response', function () { + let expectedResponse = [ + { + 'requestId': '2e42361a6172bf', + 'cpm': 0.0920, + 'width': 300, + 'height': 250, + 'netRevenue': true, + 'currency': 'USD', + 'creativeId': '', + 'ttl': 700, + 'ad': '' + } + ]; + let request = spec.buildRequests(bidRequests)[0]; + let result = spec.interpretResponse({body: response}, request); + expect(Object.keys(result[0])).to.have.members(Object.keys(expectedResponse[0])); + expect(result[0].cpm).to.not.equal(null); + expect(result[0].creativeId).to.not.equal(null); + expect(result[0].ad).to.not.equal(null); + expect(result[0].currency).to.equal('USD'); + expect(result[0].netRevenue).to.equal(true); + }); + }) +})