diff --git a/integrationExamples/gpt/pbjs_ucfunnel_gpt.html b/integrationExamples/gpt/pbjs_ucfunnel_gpt.html deleted file mode 100644 index cb33a887c23..00000000000 --- a/integrationExamples/gpt/pbjs_ucfunnel_gpt.html +++ /dev/null @@ -1,97 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - -

Prebid.js Test

-
Div-1
- - -
- -
- - - diff --git a/modules/ucfunnelBidAdapter.js b/modules/ucfunnelBidAdapter.js index c9548a280a0..d0ed7044242 100644 --- a/modules/ucfunnelBidAdapter.js +++ b/modules/ucfunnelBidAdapter.js @@ -1,21 +1,38 @@ import * as utils from 'src/utils'; import {registerBidder} from 'src/adapters/bidderFactory'; -import { BANNER } from 'src/mediaTypes'; +import {BANNER, VIDEO, NATIVE} from 'src/mediaTypes'; const VER = 'ADGENT_PREBID-2018011501'; -const BID_REQUEST_BASE_URL = '//hb.aralego.com/header'; -const UCFUNNEL_BIDDER_CODE = 'ucfunnel'; +const BIDDER_CODE = 'ucfunnel'; + +const VIDEO_CONTEXT = { + INSTREAM: 0, + OUSTREAM: 2 +} export const spec = { - code: UCFUNNEL_BIDDER_CODE, - supportedMediaTypes: [BANNER], + code: BIDDER_CODE, + ENDPOINT: '//hb.aralego.com/header', + supportedMediaTypes: [BANNER, VIDEO, NATIVE], /** * Check if the bid is a valid zone ID in either number or string form * @param {object} bid the ucfunnel bid to validate * @return boolean for whether or not a bid is valid */ isBidRequestValid: function(bid) { - return !!(bid && bid.params && bid.params.adid && typeof bid.params.adid === 'string'); + const isVideoMediaType = utils.deepAccess(bid, 'mediaTypes.video'); + const videoContext = utils.deepAccess(bid, 'mediaTypes.video.context'); + + if (typeof bid.params !== 'object' || typeof bid.params.adid != 'string') { + return false; + } + + if (isVideoMediaType && videoContext === 'outstream') { + utils.logWarn('Warning: outstream video is not supported yet'); + return false; + } + + return true; }, /** @@ -23,21 +40,15 @@ export const spec = { * @param {*} bidderRequest * @return {ServerRequest} */ - buildRequests: function(validBidRequests, bidderRequest) { - var bidRequests = []; - for (var i = 0; i < validBidRequests.length; i++) { - var bid = validBidRequests[i]; - - var ucfunnelUrlParams = buildUrlParams(bid, bidderRequest); - - bidRequests.push({ + buildRequests: function(bids, bidderRequest) { + return bids.map(bid => { + return { method: 'GET', - url: BID_REQUEST_BASE_URL, - bidRequest: bid, - data: ucfunnelUrlParams - }); - } - return bidRequests; + url: location.protocol + spec.ENDPOINT, + data: getRequestData(bid, bidderRequest), + bidRequest: bid + } + }); }, /** @@ -46,55 +57,151 @@ export const spec = { * @return {Bid[]} An array of formatted bids. */ interpretResponse: function (ucfunnelResponseObj, request) { - var bidResponses = []; - var bidRequest = request.bidRequest; - var responseBody = ucfunnelResponseObj ? ucfunnelResponseObj.body : {}; + const bidRequest = request.bidRequest; + const ad = ucfunnelResponseObj ? ucfunnelResponseObj.body : {}; + const videoPlayerSize = parseSizes(bidRequest); - bidResponses.push({ + let bid = { requestId: bidRequest.bidId, - cpm: responseBody.cpm || 0, - width: responseBody.width, - height: responseBody.height, - creativeId: responseBody.ad_id, - dealId: responseBody.deal || null, + cpm: ad.cpm || 0, + creativeId: ad.ad_id, + dealId: ad.deal || null, currency: 'USD', netRevenue: true, - ttl: 1000, - mediaType: BANNER, - ad: responseBody.adm - }); + ttl: 1000 + }; + + if (ad.creative_type) { + bid.mediaType = ad.creative_type; + } + + switch (ad.creative_type) { + case NATIVE: + let nativeAd = ad.native; + Object.assign(bid, { + width: 1, + height: 1, + native: { + title: nativeAd.title, + body: nativeAd.desc, + cta: nativeAd.ctatext, + sponsoredBy: nativeAd.sponsored, + image: nativeAd.image || nativeAd.image.url, + icon: nativeAd.icon || nativeAd.icon.url, + clickUrl: nativeAd.clickUrl, + impressionTrackers: nativeAd.impressionTrackers, + } + }); + break; + case VIDEO: + Object.assign(bid, { + vastUrl: ad.vastUrl, + vastXml: ad.vastXml + }); + + if (videoPlayerSize && videoPlayerSize.length === 2) { + Object.assign(bid, { + width: videoPlayerSize[0], + height: videoPlayerSize[1] + }); + } + break; + case BANNER: + default: + Object.assign(bid, { + width: ad.width, + height: ad.height, + ad: ad.adm + }); + } + + return [bid]; + }, - return bidResponses; + getUserSyncs: function(syncOptions) { + if (syncOptions.iframeEnabled) { + return [{ + type: 'iframe', + url: '//cdn.aralego.com/ucfad/cookie/sync.html' + }]; + } else if (syncOptions.pixelEnabled) { + return [{ + type: 'image', + url: '//sync.aralego.com/idSync' + }]; + } } }; registerBidder(spec); -function buildUrlParams(bid, bidderRequest) { - const host = utils.getTopWindowLocation().host; - const page = utils.getTopWindowLocation().pathname; - const refer = document.referrer; +function transformSizes(requestSizes) { + if (utils.isArray(requestSizes) && requestSizes.length === 2 && !utils.isArray(requestSizes[0])) { + return [parseInt(requestSizes[0], 10), parseInt(requestSizes[1], 10)]; + } else if (typeof requestSizes === 'object' && requestSizes.length) { + return requestSizes[0]; + } +} + +function parseSizes(bid) { + let params = bid.params; + if (bid.mediaType === VIDEO) { + let size = []; + if (params.video && params.video.playerWidth && params.video.playerHeight) { + size = [ + params.video.playerWidth, + params.video.playerHeight + ]; + return size; + } + } + + return transformSizes(bid.sizes); +} + +function getRequestData(bid, bidderRequest) { + const size = parseSizes(bid); + const loc = utils.getTopWindowLocation(); + const host = loc.host; + const page = loc.href; + const ref = utils.getTopWindowReferrer(); const language = navigator.language; const dnt = (navigator.doNotTrack == 'yes' || navigator.doNotTrack == '1' || navigator.msDoNotTrack == '1') ? 1 : 0; + const videoContext = utils.deepAccess(bid, 'mediaTypes.video.context'); + const videoMediaType = utils.deepAccess(bid, 'mediaTypes.video'); - let queryString = [ - 'ifr', '0', - 'bl', language, - 'je', '1', - 'dnt', dnt, - 'host', host, - 'u', page, - 'ru', refer, - 'adid', utils.getBidIdParameter('adid', bid.params), - 'ver', VER - ]; + // general bid data + let bidData = { + ver: VER, + ifr: 0, + bl: language, + je: 1, + dnt: dnt, + host: host, + u: page, + ru: ref, + adid: utils.getBidIdParameter('adid', bid.params), + w: size[0], + h: size[1] + }; + + if (bid.mediaType === 'video' || videoMediaType) { + switch (videoContext) { + case 'outstream': + bidData.atype = VIDEO_CONTEXT.OUSTREAM; + break; + case 'instream': + default: + bidData.atype = VIDEO_CONTEXT.INSTREAM; + break; + } + } if (bidderRequest && bidderRequest.gdprConsent) { - queryString.push('gdpr', bidderRequest.gdprConsent.gdprApplies ? 1 : 0); - queryString.push('euconsent', bidderRequest.gdprConsent.consentString); + Object.assign(bidData, { + gdpr: bidderRequest.gdprConsent.gdprApplies ? 1 : 0, + euconsent: bidderRequest.gdprConsent.consentString + }); } - return queryString.reduce( - (memo, curr, index) => - index % 2 === 0 && queryString[index + 1] !== undefined ? memo + curr + '=' + encodeURIComponent(queryString[index + 1]) + '&' : memo, '' - ).slice(0, -1); + return bidData; } diff --git a/test/spec/modules/ucfunnelBidAdapter_spec.js b/test/spec/modules/ucfunnelBidAdapter_spec.js index 152c7c39b1e..e8a4624bf16 100644 --- a/test/spec/modules/ucfunnelBidAdapter_spec.js +++ b/test/spec/modules/ucfunnelBidAdapter_spec.js @@ -1,19 +1,20 @@ import { expect } from 'chai'; import { spec } from 'modules/ucfunnelBidAdapter'; +import {BANNER, VIDEO, NATIVE} from 'src/mediaTypes'; const URL = '//hb.aralego.com/header'; const BIDDER_CODE = 'ucfunnel'; -const validBidReq = { +const validBannerBidReq = { bidder: BIDDER_CODE, params: { - adid: 'test-ad-83444226E44368D1E32E49EEBE6D29' + adid: 'ad-34BBD2AA24B678BBFD4E7B9EE3B872D' }, sizes: [[300, 250]], bidId: '263be71e91dd9d', auctionId: '9ad1fa8d-2297-4660-a018-b39945054746', }; -const invalidBidReq = { +const invalidBannerBidReq = { bidder: BIDDER_CODE, params: { adid: 123456789 @@ -23,86 +24,166 @@ const invalidBidReq = { auctionId: '9ad1fa8d-2297-4660-a018-b39945054746' }; -const bidReq = [{ +const validBannerBidRes = { + creative_type: BANNER, + ad_id: 'ad-34BBD2AA24B678BBFD4E7B9EE3B872D', + adm: '
', + cpm: 0.01, + height: 250, + width: 300 +}; + +const validVideoBidReq = { bidder: BIDDER_CODE, params: { - adid: 'test-ad-83444226E44368D1E32E49EEBE6D29' + adid: 'ad-9A22D466494297EAC443D967B2622DA9' }, - sizes: [[300, 250]], - bidId: '263be71e91dd9d', - auctionId: '9ad1fa8d-2297-4660-a018-b39945054746' -}]; + sizes: [[640, 360]], + bidId: '263be71e91dd9f', + auctionId: '9ad1fa8d-2297-4660-a018-b39945054746', +}; -const validBidRes = { - ad_id: 'ad-83444226E44368D1E32E49EEBE6D29', - adm: '
', +const validVideoBidRes = { + creative_type: VIDEO, + ad_id: 'ad-9A22D466494297EAC443D967B2622DA9', + vastUrl: 'https://ads.aralego.com/ads/58f9749f-0553-4993-8d9a-013a38b29e55', + vastXml: 'ucX I-Primo 00:00:30', cpm: 0.01, - height: 250, - width: 300 + width: 640, + height: 360 }; -const bidResponse = validBidRes; - -const bidResArray = [ - validBidRes, - { - ad: '', - bidRequestId: '263be71e91dd9d', - cpm: 100, - adId: '123abc', - currency: 'USD', - netRevenue: true, - width: 300, - height: 250, - ttl: 360 +const validNativeBidReq = { + bidder: BIDDER_CODE, + params: { + adid: 'ad-627736446B2BD3A60E8AEABDB7BD833E' }, - { - ad: '
Hello
', - bidRequestId: '', - cpm: 0, - adId: '123abc', - currency: 'USD', - netRevenue: true, - width: 300, - height: 250, - ttl: 360 - } -]; + sizes: [[1, 1]], + bidId: '263be71e91dda0', + auctionId: '9ad1fa8d-2297-4660-a018-b39945054746', +}; + +const validNativeBidRes = { + creative_type: NATIVE, + ad_id: 'ad-9A22D466494297EAC443D967B2622DA9', + native: { + title: 'ucfunnel adExchange', + body: 'We monetize your traffic via historic data driven protocol', + cta: 'Learn more', + sponsoredBy: 'ucfunnel Co., Ltd.', + image: { + url: 'https://cdn.aralego.net/img/main/AdGent-1200x627.jpg', + width: 1200, + height: 627 + }, + icon: { + url: 'https://cdn.aralego.net/img/logo/logo-84x84.jpg', + widt: 84, + heigh: 84 + }, + clickUrl: 'https://www.ucfunnel.com', + impressionTrackers: ['https://www.aralego.net/imp?mf=native&adid=ad-9A22D466494297EAC443D967B2622DA9&auc=9ad1fa8d-2297-4660-a018-b39945054746'], + }, + cpm: 0.01, + height: 1, + width: 1 +}; describe('ucfunnel Adapter', () => { describe('request', () => { it('should validate bid request', () => { - expect(spec.isBidRequestValid(validBidReq)).to.equal(true); + expect(spec.isBidRequestValid(validBannerBidReq)).to.equal(true); }); it('should not validate incorrect bid request', () => { - expect(spec.isBidRequestValid(invalidBidReq)).to.equal(false); + expect(spec.isBidRequestValid(invalidBannerBidReq)).to.equal(false); }); }); describe('build request', () => { - it('Verify bid request', () => { - const request = spec.buildRequests(bidReq); + const request = spec.buildRequests([validBannerBidReq]); + it('should create a POST request for every bid', () => { expect(request[0].method).to.equal('GET'); - expect(request[0].url).to.equal(URL); - expect(request[0].data).to.match(new RegExp(`${bidReq[0].params.adid}`)); + expect(request[0].url).to.equal(location.protocol + spec.ENDPOINT); + }); + + it('should attach the bid request object', () => { + expect(request[0].bidRequest).to.equal(validBannerBidReq); + }); + + it('should attach request data', () => { + const data = request[0].data; + const [ width, height ] = validBannerBidReq.sizes[0]; + expect(data.w).to.equal(width); + expect(data.h).to.equal(height); + }); + + it('must parse bid size from a nested array', () => { + const width = 640; + const height = 480; + validBannerBidReq.sizes = [[ width, height ]]; + const requests = spec.buildRequests([ validBannerBidReq ]); + const data = requests[0].data; + expect(data.w).to.equal(width); + expect(data.h).to.equal(height); }); }); describe('interpretResponse', () => { - it('should build bid array', () => { - const request = spec.buildRequests(bidReq); - const result = spec.interpretResponse({body: bidResponse}, request[0]); - expect(result.length).to.equal(1); + describe('should support banner', () => { + const request = spec.buildRequests([ validBannerBidReq ]); + const result = spec.interpretResponse({body: validBannerBidRes}, request[0]); + it('should build bid array for banner', () => { + expect(result.length).to.equal(1); + }); + + it('should have all relevant fields', () => { + const bid = result[0]; + + expect(bid.mediaType).to.equal(BANNER); + expect(bid.ad).to.exist; + expect(bid.requestId).to.equal('263be71e91dd9d'); + expect(bid.cpm).to.equal(0.01); + expect(bid.width).to.equal(300); + expect(bid.height).to.equal(250); + }); + }); + + describe('should support video', () => { + const request = spec.buildRequests([ validVideoBidReq ]); + const result = spec.interpretResponse({body: validVideoBidRes}, request[0]); + it('should build bid array', () => { + expect(result.length).to.equal(1); + }); + + it('should have all relevant fields', () => { + const bid = result[0]; + + expect(bid.mediaType).to.equal(VIDEO); + expect(bid.vastUrl).to.exist; + expect(bid.vastXml).to.exist; + expect(bid.requestId).to.equal('263be71e91dd9f'); + expect(bid.cpm).to.equal(0.01); + expect(bid.width).to.equal(640); + expect(bid.height).to.equal(360); + }); }); - it('should have all relevant fields', () => { - const request = spec.buildRequests(bidReq); - const result = spec.interpretResponse({body: bidResponse}, request[0]); - const bid = result[0]; + describe('should support native', () => { + const request = spec.buildRequests([ validNativeBidReq ]); + const result = spec.interpretResponse({body: validNativeBidRes}, request[0]); + it('should build bid array', () => { + expect(result.length).to.equal(1); + }); + + it('should have all relevant fields', () => { + const bid = result[0]; - expect(bid.requestId).to.equal('263be71e91dd9d'); - expect(bid.cpm).to.equal(0.01); - expect(bid.width).to.equal(300); - expect(bid.height).to.equal(250); + expect(bid.mediaType).to.equal(NATIVE); + expect(bid.native).to.exist; + expect(bid.requestId).to.equal('263be71e91dda0'); + expect(bid.cpm).to.equal(0.01); + expect(bid.width).to.equal(1); + expect(bid.height).to.equal(1); + }); }); }); });