diff --git a/modules/lifestreetBidAdapter.js b/modules/lifestreetBidAdapter.js new file mode 100644 index 00000000000..4317eb8b82e --- /dev/null +++ b/modules/lifestreetBidAdapter.js @@ -0,0 +1,139 @@ +import * as utils from '../src/utils.js'; +import { registerBidder } from '../src/adapters/bidderFactory.js'; +import { BANNER, VIDEO } from '../src/mediaTypes.js'; + +const BIDDER_CODE = 'lifestreet'; +const ADAPTER_VERSION = '$prebid.version$'; + +const urlTemplate = template`https://ads.lfstmedia.com/gate/${'adapter'}/${'slot'}?adkey=${'adkey'}&ad_size=${'ad_size'}&__location=${'location'}&__referrer=${'referrer'}&__wn=${'wn'}&__sf=${'sf'}&__fif=${'fif'}&__if=${'if'}&__stamp=${'stamp'}&__pp=1&__hb=1&_prebid_json=1&__gz=1&deferred_format=vast_2_0,vast_3_0&__hbver=${'hbver'}`; + +/** + * A helper function for template to generate string from boolean + */ +function boolToString(value) { + return value ? '1' : '0'; +} + +/** + * A helper function to form URL from the template + */ +function template(strings, ...keys) { + return function(...values) { + let dict = values[values.length - 1] || {}; + let result = [strings[0]]; + keys.forEach(function(key, i) { + let value = utils.isInteger(key) ? values[key] : dict[key]; + result.push(value, strings[i + 1]); + }); + return result.join(''); + }; +} + +/** + * Creates a bid requests for a given bid. + * + * @param {BidRequest} bid The bid params to use for formatting a request + */ +function formatBidRequest(bid, bidderRequest = {}) { + const {params} = bid; + const {referer} = (bidderRequest.refererInfo || {}); + let url = urlTemplate({ + adapter: 'prebid', + slot: params.slot, + adkey: params.adkey, + ad_size: params.ad_size, + location: referer, + referrer: referer, + wn: boolToString(/fb_http/i.test(window.name)), + sf: boolToString(window['sfAPI'] || window['$sf']), + fif: boolToString(window['inDapIF'] === true), + if: boolToString(window !== window.top), + stamp: new Date().getTime(), + hbver: ADAPTER_VERSION + }); + + if (bidderRequest.gdprConsent) { + if (bidderRequest.gdprConsent.gdprApplies !== undefined) { + const gdpr = '&__gdpr=' + (bidderRequest.gdprConsent.gdprApplies ? '1' : '0'); + url += gdpr; + } + if (bidderRequest.gdprConsent.consentString !== undefined) { + url += `&__consent=${bidderRequest.gdprConsent.consentString}`; + } + } + + // ccpa support + if (bidderRequest.uspConsent) { + url += `&__us_privacy=${bidderRequest.uspConsent}` + } + + return { + method: 'GET', + url: url, + bidId: bid.bidId + }; +} + +function isResponseValid(response) { + return !/^\s*\{\s*"advertisementAvailable"\s*:\s*false/i.test(response.content) && + response.content.indexOf('') === -1 && (typeof response.cpm !== 'undefined') && + response.status === 1; +} + +export const spec = { + code: BIDDER_CODE, + aliases: ['lsm'], + supportedMediaTypes: [BANNER, VIDEO], + + isBidRequestValid: (bid = {}) => { + const {params = {}} = bid; + return !!(params.slot && params.adkey && params.ad_size); + }, + + buildRequests: (validBidRequests, bidderRequest) => { + return validBidRequests.map(bid => { + return formatBidRequest(bid, bidderRequest) + }); + }, + + interpretResponse: (serverResponse, bidRequest) => { + const bidResponses = []; + let response = serverResponse.body; + + if (!isResponseValid(response)) { + return bidResponses; + } + + const bidResponse = { + requestId: bidRequest.bidId, + cpm: response.cpm, + width: response.width, + height: response.height, + creativeId: response.creativeId, + currency: response.currency ? response.currency : 'USD', + netRevenue: response.netRevenue ? response.netRevenue : true, + ttl: response.ttl ? response.ttl : 86400 + }; + + if (response.hasOwnProperty('dealId')) { + bidResponse.dealId = response.dealId; + } + if (response.content_type.indexOf('vast') > -1) { + if (typeof response.vastUrl !== 'undefined') { + bidResponse.vastUrl = response.vastUrl; + } else { + bidResponse.vastXml = response.content; + } + + bidResponse.mediaType = VIDEO; + } else { + bidResponse.ad = response.content; + bidResponse.mediaType = BANNER; + } + + bidResponses.push(bidResponse); + return bidResponses; + } +}; + +registerBidder(spec); diff --git a/modules/lifestreetBidAdapter.md b/modules/lifestreetBidAdapter.md index 58cf4a533a9..a874792d84c 100644 --- a/modules/lifestreetBidAdapter.md +++ b/modules/lifestreetBidAdapter.md @@ -1,22 +1,41 @@ # Overview +``` Module Name: Lifestreet Bid Adapter - Module Type: Lifestreet Adapter - Maintainer: hb.tech@lifestreet.com +``` # Description Module that connects to Lifestreet's demand sources +Values, listed in `ALL_BANNER_SIZES` and `ALL_VIDEO_SIZES` are all the values which our server supports. +For `ad_size`, please use one of that values in following format: `ad_size: WIDTHxHEIGHT` + # Test Parameters ```javascript - var adUnits = [ - // Banner adUnit + const ALL_BANNER_SIZES = [ + [120, 600], [160, 600], [300, 250], [300, 600], [320, 480], + [320, 50], [468, 60], [510, 510], [600, 300], + [720, 300], [728, 90], [760, 740], [768, 1024] + ]; + + const ALL_VIDEO_SIZES = [ + [640, 480], [650, 520], [970, 580] + ] +``` + +# Test Parameters (Banner) +``` + const adUnits = [ { code: 'test-ad', - sizes: [[160, 600]], + mediaTypes: { + banner: { + sizes: [[160, 600]], + } + }, bids: [ { bidder: 'lifestreet', @@ -28,10 +47,20 @@ Module that connects to Lifestreet's demand sources } ] }, - // Video instream adUnit + ]; +``` + +# Test Parameters (Video) +``` + const adUnits = [ { code: 'test-video-ad', - sizes: [[640, 480]], + mediaTypes: { + video: { + playerSize: [[640, 480]], + context: 'instream' + } + }, bids: [ { bidder: 'lifestreet', diff --git a/test/spec/modules/lifestreetBidAdapter_spec.js b/test/spec/modules/lifestreetBidAdapter_spec.js new file mode 100644 index 00000000000..d66727da644 --- /dev/null +++ b/test/spec/modules/lifestreetBidAdapter_spec.js @@ -0,0 +1,232 @@ +import { expect } from 'chai'; +import { BANNER, VIDEO } from 'src/mediaTypes.js'; +import { spec } from 'modules/lifestreetBidAdapter.js'; + +describe('lifestreetBidAdapter', function() { + let bidRequests; + let videoBidRequests; + let bidResponses; + let videoBidResponses; + beforeEach(function() { + bidRequests = [ + { + bidder: 'lifestreet', + params: { + slot: 'slot166704', + adkey: '78c', + ad_size: '160x600' + }, + mediaTypes: { + banner: { + sizes: [ + [160, 600], + [300, 600] + ] + } + }, + sizes: [ + [160, 600], + [300, 600] + ] + } + ]; + + bidResponses = { + body: { + cpm: 0.1, + netRevenue: true, + content_type: 'display_flash', + width: 160, + currency: 'USD', + ttl: 86400, + content: '