From 6034d5761b3f390c57500bad3728990ffd61e434 Mon Sep 17 00:00:00 2001 From: Ruslan Sibgatullin Date: Fri, 30 Sep 2022 07:20:21 +0200 Subject: [PATCH 1/2] PREB-36 [Prebid.js] Native support --- modules/smaatoBidAdapter.js | 143 ++++++- modules/smaatoBidAdapter.md | 44 +++ test/spec/modules/smaatoBidAdapter_spec.js | 411 +++++++++++++++++++-- 3 files changed, 557 insertions(+), 41 deletions(-) diff --git a/modules/smaatoBidAdapter.js b/modules/smaatoBidAdapter.js index c783f35d915..2adf6a81eca 100644 --- a/modules/smaatoBidAdapter.js +++ b/modules/smaatoBidAdapter.js @@ -1,13 +1,43 @@ -import { deepAccess, getDNT, deepSetValue, logInfo, logError, isEmpty, getAdUnitSizes, fill, chunk, getMaxValueFromArray, getMinValueFromArray } from '../src/utils.js'; +import { cleanObj, deepAccess, getDNT, deepSetValue, logInfo, logError, isEmpty, getAdUnitSizes, fill, chunk, getMaxValueFromArray, getMinValueFromArray } from '../src/utils.js'; import {registerBidder} from '../src/adapters/bidderFactory.js'; import {config} from '../src/config.js'; -import {ADPOD, BANNER, VIDEO} from '../src/mediaTypes.js'; +import {ADPOD, BANNER, VIDEO, NATIVE} from '../src/mediaTypes.js'; const BIDDER_CODE = 'smaato'; const SMAATO_ENDPOINT = 'https://prebid.ad.smaato.net/oapi/prebid'; -const SMAATO_CLIENT = 'prebid_js_$prebid.version$_1.6' +const SMAATO_CLIENT = 'prebid_js_$prebid.version$_1.7' const CURRENCY = 'USD'; +const NATIVE_DATA = { + ASSET_TYPES: { + TITLE: 'title', + IMG: 'img', + DATA: 'data', + }, + ASSETS: { + title: {id: 0, assetType: 'title'}, + + image: {id: 4, assetType: 'img', type: 3, name: 'image'}, + icon: {id: 2, assetType: 'img', type: 2, name: 'icon'}, + + sponsoredBy: {id: 1, assetType: 'data', type: 1, name: 'sponsoredBy'}, + body: {id: 3, assetType: 'data', type: 2, name: 'body'}, + rating: {id: 5, assetType: 'data', type: 3, name: 'rating'}, + likes: {id: 6, assetType: 'data', type: 4, name: 'likes'}, + downloads: {id: 7, assetType: 'data', type: 5, name: 'downloads'}, + price: {id: 8, assetType: 'data', type: 6, name: 'price'}, + salePrice: {id: 9, assetType: 'data', type: 7, name: 'salePrice'}, + phone: {id: 10, assetType: 'data', type: 8, name: 'phone'}, + address: {id: 11, assetType: 'data', type: 9, name: 'address'}, + body2: {id: 12, assetType: 'data', type: 10, name: 'body2'}, + displayUrl: {id: 13, assetType: 'data', type: 11, name: 'displayUrl'}, + cta: {id: 14, assetType: 'data', type: 12, name: 'cta'}, + }, + getAssetById(id) { + return Object.values(this.ASSETS).find(asset => id === asset.id); + } +}; + const buildOpenRtbBidRequest = (bidRequest, bidderRequest) => { const requestTemplate = { id: bidderRequest.auctionId, @@ -91,6 +121,12 @@ const buildOpenRtbBidRequest = (bidRequest, bidderRequest) => { } } + const nativeMediaType = deepAccess(bidRequest, 'mediaTypes.native'); + if (nativeMediaType) { + const nativeRequest = Object.assign({}, requestTemplate, createNativeImp(bidRequest, nativeMediaType)); + requests.push(nativeRequest); + } + return requests; } @@ -109,11 +145,11 @@ const buildServerRequest = (validBidRequest, data) => { export const spec = { code: BIDDER_CODE, - supportedMediaTypes: [BANNER, VIDEO], + supportedMediaTypes: [BANNER, VIDEO, NATIVE], gvlid: 82, /** - * Determines whether or not the given bid request is valid. + * Determines whether 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. @@ -171,6 +207,7 @@ export const spec = { * Unpack the response from the server into a list of bids. * * @param {ServerResponse} serverResponse A successful response from the server. + * @param {BidRequest} bidRequest * @return {Bid[]} An array of bids which were nested inside the server. */ interpretResponse: (serverResponse, bidRequest) => { @@ -239,6 +276,11 @@ export const spec = { resultingBid.mediaType = VIDEO; bids.push(resultingBid); break; + case 'Native': + resultingBid.native = createNativeAd(bid.adm); + resultingBid.mediaType = NATIVE; + bids.push(resultingBid); + break; default: logInfo('[SMAATO] Invalid ad type:', smtAdType); } @@ -297,6 +339,48 @@ const createRichmediaAd = (adm) => { return markup + ''; }; +const createNativeAd = (adm) => { + const nativeResponse = JSON.parse(adm).native; + const nativeAd = { + clickUrl: deepAccess(nativeResponse, 'link.url'), + clickTrackers: deepAccess(nativeResponse, 'link.clicktrackers'), + privacyLink: nativeResponse.privacy, + impressionTrackers: [], + } + + nativeResponse.eventtrackers.forEach(tracker => { + if (tracker.event !== 1) return; + switch (tracker.method) { + case 1: // img + nativeAd.impressionTrackers.push(tracker.url); + break; + case 2: // js + nativeAd.javascriptTrackers = ``; + break; + } + }); + + nativeResponse.assets.map(asset => { + const assetParams = NATIVE_DATA.getAssetById(asset.id); + switch (assetParams.assetType) { + case NATIVE_DATA.ASSET_TYPES.TITLE: + nativeAd.title = asset.title.text; + break; + case NATIVE_DATA.ASSET_TYPES.DATA: + nativeAd[assetParams.name] = asset.data.value; + break; + case NATIVE_DATA.ASSET_TYPES.IMG: + nativeAd[assetParams.name] = { + url: asset.img.url, + width: asset.img.w, + height: asset.img.h, + }; + break; + } + }); + return nativeAd; +}; + function createBannerImp(bidRequest) { const adUnitSizes = getAdUnitSizes(bidRequest); const sizes = adUnitSizes.map((size) => ({w: size[0], h: size[1]})); @@ -342,6 +426,55 @@ function createVideoImp(bidRequest, videoMediaType) { }; } +function createNativeImp(bidRequest, nativeMediaType) { + const mainImageSize = nativeMediaType.image.sizes + const assets = [] + + for (const nativeAssetName of Object.keys(nativeMediaType)) { + const assetFromParams = NATIVE_DATA.ASSETS[nativeAssetName]; + if (assetFromParams) { + const assetParams = nativeMediaType[nativeAssetName]; + const asset = { + id: assetFromParams.id, + required: Number(assetParams.required), + }; + switch (assetFromParams.assetType) { + case NATIVE_DATA.ASSET_TYPES.TITLE: + asset.title = {len: assetParams.len} + break; + case NATIVE_DATA.ASSET_TYPES.DATA: + asset.data = cleanObj({type: assetFromParams.type, len: assetParams.len}) + break; + case NATIVE_DATA.ASSET_TYPES.IMG: + asset.img = cleanObj({ + type: assetFromParams.type, + w: deepAccess(assetParams, 'sizes.0'), + h: deepAccess(assetParams, 'sizes.1'), + wmin: deepAccess(assetParams, 'aspect_ratios.0.min_width'), + hmin: deepAccess(assetParams, 'aspect_ratios.0.min_height') + }); + break; + default: + return; + } + assets.push(asset); + } + } + + return { + imp: [{ + id: bidRequest.bidId, + tagid: deepAccess(bidRequest, 'params.adspaceId'), + bidfloor: getBidFloor(bidRequest, NATIVE, mainImageSize), + native: { + ver: '1.2', + privacy: nativeMediaType.privacyLink ? 1 : 0, + assets: assets + } + }] + }; +} + function createAdPodImp(bidRequest, videoMediaType) { const tagid = deepAccess(bidRequest, 'params.adbreakId') const bce = config.getConfig('adpod.brandCategoryExclusion') diff --git a/modules/smaatoBidAdapter.md b/modules/smaatoBidAdapter.md index 41e1c952f2a..6be7fbb9a3d 100644 --- a/modules/smaatoBidAdapter.md +++ b/modules/smaatoBidAdapter.md @@ -63,6 +63,50 @@ var adUnits = [{ }]; ``` +For native adunits: + +``` +var adUnits = [{ + "code": "native unit", + "mediaTypes": { + native: { + sendTargetingKeys: false, + image: { + required: true, + sizes: [150, 50] + }, + title: { + required: true, + len: 80 + }, + sponsoredBy: { + required: true + }, + body: { + required: true + }, + cta: { + required: false + }, + rating: { + required: false + }, + icon: { + required: true, + sizes: [50, 50] + } + } + }, + "bids": [{ + "bidder": "smaato", + "params": { + "publisherId": "1100042525", + "adspaceId": "130563103" + } + }] +}]; +``` + For adpod adunits: ``` diff --git a/test/spec/modules/smaatoBidAdapter_spec.js b/test/spec/modules/smaatoBidAdapter_spec.js index 99592765845..2bbf395d0ae 100644 --- a/test/spec/modules/smaatoBidAdapter_spec.js +++ b/test/spec/modules/smaatoBidAdapter_spec.js @@ -6,6 +6,7 @@ import {createEidsArray} from 'modules/userId/eids.js'; const ADTYPE_IMG = 'Img'; const ADTYPE_RICHMEDIA = 'Richmedia'; const ADTYPE_VIDEO = 'Video'; +const ADTYPE_NATIVE = 'Native'; const REFERRER = 'http://example.com/page.html' const CONSENT_STRING = 'HFIDUYFIUYIUYWIPOI87392DSU' @@ -421,7 +422,6 @@ describe('smaatoBidAdapterTest', () => { }, adUnitCode: '/19968336/header-bid-tag-0', transactionId: 'transactionId', - sizes: [[300, 50]], bidId: 'bidId', bidderRequestId: 'bidderRequestId', src: 'client', @@ -793,6 +793,212 @@ describe('smaatoBidAdapterTest', () => { }); }); + describe('buildRequests for native imps', () => { + const NATIVE_PREBID_MEDIATYPE = { + image: { + required: true, + sizes: [150, 50], + aspect_ratios: [{ + min_width: 100, + min_height: 25 + }] + }, + icon: { + required: true, + sizes: [50, 50] + }, + title: { + required: true, + len: 80 + }, + sponsoredBy: { + required: true + }, + body: { + required: true + }, + rating: { + required: true + }, + likes: { + required: true + }, + downloads: { + required: true + }, + price: { + required: true + }, + salePrice: { + required: true + }, + phone: { + required: false + }, + address: { + required: true + }, + body2: { + required: false + }, + displayUrl: { + required: false + }, + cta: { + required: true + }, + privacyLink: { + required: false + } + }; + const NATIVE_OPENRTB_IMP = { + ver: '1.2', + privacy: 1, + assets: [ + { + 'id': 4, + 'required': 1, + 'img': { + 'type': 3, + 'w': 150, + 'h': 50, + 'wmin': 100, + 'hmin': 25 + } + }, + { + 'id': 2, + 'required': 1, + 'img': { + 'type': 2, + 'w': 50, + 'h': 50 + } + }, + { + 'id': 0, + 'required': 1, + 'title': { + 'len': 80 + } + }, + { + 'id': 1, + 'required': 1, + 'data': { + 'type': 1 + } + }, + { + 'id': 3, + 'required': 1, + 'data': { + 'type': 2 + } + }, + { + 'id': 5, + 'required': 1, + 'data': { + 'type': 3 + } + }, + { + 'id': 6, + 'required': 1, + 'data': { + 'type': 4 + } + }, + { + 'id': 7, + 'required': 1, + 'data': { + 'type': 5 + } + }, + { + 'id': 8, + 'required': 1, + 'data': { + 'type': 6 + } + }, + { + 'id': 9, + 'required': 1, + 'data': { + 'type': 7 + } + }, + { + 'id': 10, + 'required': 0, + 'data': { + 'type': 8 + } + }, + { + 'id': 11, + 'required': 1, + 'data': { + 'type': 9 + } + }, + { + 'id': 12, + 'required': 0, + 'data': { + 'type': 10 + } + }, + { + 'id': 13, + 'required': 0, + 'data': { + 'type': 11 + } + }, + { + 'id': 14, + 'required': 1, + 'data': { + 'type': 12 + } + } + ] + }; + + const singleNativeBidRequest = { + bidder: 'smaato', + params: { + publisherId: 'publisherId', + adspaceId: 'adspaceId' + }, + mediaTypes: { + native: NATIVE_PREBID_MEDIATYPE + }, + adUnitCode: '/19968336/header-bid-tag-0', + transactionId: 'transactionId', + bidId: 'bidId', + bidderRequestId: 'bidderRequestId', + src: 'client', + bidRequestsCount: 1, + bidderRequestsCount: 1, + bidderWinsCount: 0 + }; + + it('sends correct native imps', () => { + const reqs = spec.buildRequests([singleNativeBidRequest], defaultBidderRequest); + + const req = extractPayloadOfFirstAndOnlyRequest(reqs); + expect(req.imp[0].id).to.be.equal('bidId'); + expect(req.imp[0].tagid).to.be.equal('adspaceId'); + expect(req.imp[0].bidfloor).to.be.undefined; + expect(req.imp[0].native).to.deep.equal(NATIVE_OPENRTB_IMP); + }); + }); + describe('in-app requests', () => { const LOCATION = { lat: 33.3, @@ -937,45 +1143,128 @@ describe('smaatoBidAdapterTest', () => { switch (adType) { case ADTYPE_IMG: - adm = JSON.stringify({ - image: { - img: { - url: 'https://prebid/static/ad.jpg', - w: 320, - h: 50, - ctaurl: 'https://prebid/track/ctaurl' - }, - impressiontrackers: [ - 'https://prebid/track/imp/1', - 'https://prebid/track/imp/2' - ], - clicktrackers: [ - 'https://prebid/track/click/1' - ] - } - }); + adm = JSON.stringify( + { + image: { + img: { + url: 'https://prebid/static/ad.jpg', + w: 320, + h: 50, + ctaurl: 'https://prebid/track/ctaurl' + }, + impressiontrackers: [ + 'https://prebid/track/imp/1', + 'https://prebid/track/imp/2' + ], + clicktrackers: [ + 'https://prebid/track/click/1' + ] + } + }); break; case ADTYPE_RICHMEDIA: - adm = JSON.stringify({ - richmedia: { - mediadata: { - content: '

RICHMEDIA CONTENT

', - w: 800, - h: 600 - }, - impressiontrackers: [ - 'https://prebid/track/imp/1', - 'https://prebid/track/imp/2' - ], - clicktrackers: [ - 'https://prebid/track/click/1' - ] - } - }); + adm = JSON.stringify( + { + richmedia: { + mediadata: { + content: '

RICHMEDIA CONTENT

', + w: 800, + h: 600 + }, + impressiontrackers: [ + 'https://prebid/track/imp/1', + 'https://prebid/track/imp/2' + ], + clicktrackers: [ + 'https://prebid/track/click/1' + ] + } + }); break; case ADTYPE_VIDEO: adm = ''; break; + case ADTYPE_NATIVE: + adm = JSON.stringify( + { + 'native': { + 'ver': '1.2', + 'link': { + 'url': 'https://link.url', + 'clicktrackers': [ + 'http://click.url/v1/click?e=prebid' + ] + }, + 'assets': [ + { + 'id': 0, + 'required': 1, + 'title': { + 'text': 'Title' + } + }, + { + 'id': 2, + 'required': 1, + 'img': { + 'type': 1, + 'url': 'https://logo.png', + 'w': 40, + 'h': 40 + } + }, + { + 'id': 4, + 'required': 1, + 'img': { + 'type': 3, + 'url': 'https://main.png', + 'w': 480, + 'h': 320 + } + }, + { + 'id': 3, + 'required': 1, + 'data': { + 'type': 2, + 'value': 'Desc' + } + }, + { + 'id': 14, + 'required': 1, + 'data': { + 'type': 12, + 'value': 'CTAText' + } + }, + { + 'id': 5, + 'required': 0, + 'data': { + 'type': 3, + 'value': '2 stars' + } + } + ], + 'eventtrackers': [ + { + 'event': 2, + 'method': 1, + 'url': 'https://js.url' + }, + { + 'event': 1, + 'method': 1, + 'url': 'http://view.url/v1/view?e=prebid' + } + ], + 'privacy': 'https://privacy.com/' + } + } + ) + break; default: throw Error('Invalid AdType'); } @@ -1030,7 +1319,7 @@ describe('smaatoBidAdapterTest', () => { }); describe('non ad pod', () => { - it('single image reponse', () => { + it('single image response', () => { const bids = spec.interpretResponse(buildOpenRtbBidResponse(ADTYPE_IMG), buildBidRequest()); expect(bids).to.deep.equal([ @@ -1056,7 +1345,7 @@ describe('smaatoBidAdapterTest', () => { ]); }); - it('single richmedia reponse', () => { + it('single richmedia response', () => { const bids = spec.interpretResponse(buildOpenRtbBidResponse(ADTYPE_RICHMEDIA), buildBidRequest()); expect(bids).to.deep.equal([ @@ -1108,6 +1397,56 @@ describe('smaatoBidAdapterTest', () => { ]); }); + it('single native response', () => { + const bids = spec.interpretResponse(buildOpenRtbBidResponse(ADTYPE_NATIVE), buildBidRequest()); + + const nativeResponse = { + 'body': 'Desc', + 'clickTrackers': [ + 'http://click.url/v1/click?e=prebid' + ], + 'clickUrl': 'https://link.url', + 'cta': 'CTAText', + 'icon': { + 'height': 40, + 'url': 'https://logo.png', + 'width': 40 + }, + 'image': { + 'height': 320, + 'url': 'https://main.png', + 'width': 480 + }, + 'impressionTrackers': [ + 'http://view.url/v1/view?e=prebid' + ], + 'privacyLink': 'https://privacy.com/', + 'rating': '2 stars', + 'title': 'Title' + } + expect(bids).to.deep.equal([ + { + requestId: '226416e6e6bf41', + cpm: 0.01, + width: 350, + height: 50, + native: nativeResponse, + ttl: 300, + creativeId: 'CR69381', + dealId: '12345', + netRevenue: true, + currency: 'USD', + mediaType: 'native', + meta: { + advertiserDomains: ['smaato.com'], + agencyId: 'CM6523', + networkName: 'smaato', + mediaType: 'native' + } + } + ]); + }); + it('ignores bid response with invalid ad type', () => { const serverResponse = buildOpenRtbBidResponse(ADTYPE_IMG); serverResponse.headers.get = (header) => { From 5b41f62a33a1dcb98288121978b5d821bcf9f49f Mon Sep 17 00:00:00 2001 From: Bernhard Pickenbrock Date: Wed, 12 Oct 2022 15:03:10 +0200 Subject: [PATCH 2/2] PREB-36 [Prebid.js] Native support - update to ortb native --- modules/smaatoBidAdapter.js | 136 ++----- modules/smaatoBidAdapter.md | 82 +++-- test/spec/modules/smaatoBidAdapter_spec.js | 398 +++++++++------------ 3 files changed, 249 insertions(+), 367 deletions(-) diff --git a/modules/smaatoBidAdapter.js b/modules/smaatoBidAdapter.js index 2adf6a81eca..18ce56c7a80 100644 --- a/modules/smaatoBidAdapter.js +++ b/modules/smaatoBidAdapter.js @@ -1,43 +1,16 @@ -import { cleanObj, deepAccess, getDNT, deepSetValue, logInfo, logError, isEmpty, getAdUnitSizes, fill, chunk, getMaxValueFromArray, getMinValueFromArray } from '../src/utils.js'; +import { deepAccess, isNumber, getDNT, deepSetValue, logInfo, logError, isEmpty, getAdUnitSizes, fill, chunk, getMaxValueFromArray, getMinValueFromArray } from '../src/utils.js'; +import {find} from '../src/polyfill.js'; import {registerBidder} from '../src/adapters/bidderFactory.js'; import {config} from '../src/config.js'; import {ADPOD, BANNER, VIDEO, NATIVE} from '../src/mediaTypes.js'; +import CONSTANTS from '../src/constants.json'; +const { NATIVE_IMAGE_TYPES } = CONSTANTS; const BIDDER_CODE = 'smaato'; const SMAATO_ENDPOINT = 'https://prebid.ad.smaato.net/oapi/prebid'; const SMAATO_CLIENT = 'prebid_js_$prebid.version$_1.7' const CURRENCY = 'USD'; -const NATIVE_DATA = { - ASSET_TYPES: { - TITLE: 'title', - IMG: 'img', - DATA: 'data', - }, - ASSETS: { - title: {id: 0, assetType: 'title'}, - - image: {id: 4, assetType: 'img', type: 3, name: 'image'}, - icon: {id: 2, assetType: 'img', type: 2, name: 'icon'}, - - sponsoredBy: {id: 1, assetType: 'data', type: 1, name: 'sponsoredBy'}, - body: {id: 3, assetType: 'data', type: 2, name: 'body'}, - rating: {id: 5, assetType: 'data', type: 3, name: 'rating'}, - likes: {id: 6, assetType: 'data', type: 4, name: 'likes'}, - downloads: {id: 7, assetType: 'data', type: 5, name: 'downloads'}, - price: {id: 8, assetType: 'data', type: 6, name: 'price'}, - salePrice: {id: 9, assetType: 'data', type: 7, name: 'salePrice'}, - phone: {id: 10, assetType: 'data', type: 8, name: 'phone'}, - address: {id: 11, assetType: 'data', type: 9, name: 'address'}, - body2: {id: 12, assetType: 'data', type: 10, name: 'body2'}, - displayUrl: {id: 13, assetType: 'data', type: 11, name: 'displayUrl'}, - cta: {id: 14, assetType: 'data', type: 12, name: 'cta'}, - }, - getAssetById(id) { - return Object.values(this.ASSETS).find(asset => id === asset.id); - } -}; - const buildOpenRtbBidRequest = (bidRequest, bidderRequest) => { const requestTemplate = { id: bidderRequest.auctionId, @@ -121,9 +94,9 @@ const buildOpenRtbBidRequest = (bidRequest, bidderRequest) => { } } - const nativeMediaType = deepAccess(bidRequest, 'mediaTypes.native'); - if (nativeMediaType) { - const nativeRequest = Object.assign({}, requestTemplate, createNativeImp(bidRequest, nativeMediaType)); + const nativeOrtbRequest = bidRequest.nativeOrtbRequest; + if (nativeOrtbRequest) { + const nativeRequest = Object.assign({}, requestTemplate, createNativeImp(bidRequest, nativeOrtbRequest)); requests.push(nativeRequest); } @@ -341,44 +314,9 @@ const createRichmediaAd = (adm) => { const createNativeAd = (adm) => { const nativeResponse = JSON.parse(adm).native; - const nativeAd = { - clickUrl: deepAccess(nativeResponse, 'link.url'), - clickTrackers: deepAccess(nativeResponse, 'link.clicktrackers'), - privacyLink: nativeResponse.privacy, - impressionTrackers: [], + return { + ortb: nativeResponse } - - nativeResponse.eventtrackers.forEach(tracker => { - if (tracker.event !== 1) return; - switch (tracker.method) { - case 1: // img - nativeAd.impressionTrackers.push(tracker.url); - break; - case 2: // js - nativeAd.javascriptTrackers = ``; - break; - } - }); - - nativeResponse.assets.map(asset => { - const assetParams = NATIVE_DATA.getAssetById(asset.id); - switch (assetParams.assetType) { - case NATIVE_DATA.ASSET_TYPES.TITLE: - nativeAd.title = asset.title.text; - break; - case NATIVE_DATA.ASSET_TYPES.DATA: - nativeAd[assetParams.name] = asset.data.value; - break; - case NATIVE_DATA.ASSET_TYPES.IMG: - nativeAd[assetParams.name] = { - url: asset.img.url, - width: asset.img.w, - height: asset.img.h, - }; - break; - } - }); - return nativeAd; }; function createBannerImp(bidRequest) { @@ -426,55 +364,33 @@ function createVideoImp(bidRequest, videoMediaType) { }; } -function createNativeImp(bidRequest, nativeMediaType) { - const mainImageSize = nativeMediaType.image.sizes - const assets = [] - - for (const nativeAssetName of Object.keys(nativeMediaType)) { - const assetFromParams = NATIVE_DATA.ASSETS[nativeAssetName]; - if (assetFromParams) { - const assetParams = nativeMediaType[nativeAssetName]; - const asset = { - id: assetFromParams.id, - required: Number(assetParams.required), - }; - switch (assetFromParams.assetType) { - case NATIVE_DATA.ASSET_TYPES.TITLE: - asset.title = {len: assetParams.len} - break; - case NATIVE_DATA.ASSET_TYPES.DATA: - asset.data = cleanObj({type: assetFromParams.type, len: assetParams.len}) - break; - case NATIVE_DATA.ASSET_TYPES.IMG: - asset.img = cleanObj({ - type: assetFromParams.type, - w: deepAccess(assetParams, 'sizes.0'), - h: deepAccess(assetParams, 'sizes.1'), - wmin: deepAccess(assetParams, 'aspect_ratios.0.min_width'), - hmin: deepAccess(assetParams, 'aspect_ratios.0.min_height') - }); - break; - default: - return; - } - assets.push(asset); - } - } - +function createNativeImp(bidRequest, nativeRequest) { return { imp: [{ id: bidRequest.bidId, tagid: deepAccess(bidRequest, 'params.adspaceId'), - bidfloor: getBidFloor(bidRequest, NATIVE, mainImageSize), + bidfloor: getBidFloor(bidRequest, NATIVE, getNativeMainImageSize(nativeRequest)), native: { - ver: '1.2', - privacy: nativeMediaType.privacyLink ? 1 : 0, - assets: assets + request: JSON.stringify(nativeRequest), + ver: '1.2' } }] }; } +function getNativeMainImageSize(nativeRequest) { + const mainImage = find(nativeRequest.assets, asset => asset.hasOwnProperty('img') && asset.img.type === NATIVE_IMAGE_TYPES.MAIN) + if (mainImage) { + if (isNumber(mainImage.img.w) && isNumber(mainImage.img.h)) { + return [[mainImage.img.w, mainImage.img.h]] + } + if (isNumber(mainImage.img.wmin) && isNumber(mainImage.img.hmin)) { + return [[mainImage.img.wmin, mainImage.img.hmin]] + } + } + return [] +} + function createAdPodImp(bidRequest, videoMediaType) { const tagid = deepAccess(bidRequest, 'params.adbreakId') const bce = config.getConfig('adpod.brandCategoryExclusion') diff --git a/modules/smaatoBidAdapter.md b/modules/smaatoBidAdapter.md index 6be7fbb9a3d..170880c3fc0 100644 --- a/modules/smaatoBidAdapter.md +++ b/modules/smaatoBidAdapter.md @@ -70,31 +70,65 @@ var adUnits = [{ "code": "native unit", "mediaTypes": { native: { - sendTargetingKeys: false, - image: { - required: true, - sizes: [150, 50] - }, - title: { - required: true, - len: 80 - }, - sponsoredBy: { - required: true - }, - body: { - required: true + ortb: { + ver: "1.2", + assets: [ + { + id: 1, + required: 1, + img: { + type: 3, + w: 150, + h: 50, + } + }, + { + id: 2, + required: 1, + img: { + type: 2, + w: 50, + h: 50 + } + }, + { + id: 3, + required: 1, + title: { + len: 80 + } + }, + { + id: 4, + required: 1, + data: { + type: 1 + } + }, + { + id: 5, + required: 1, + data: { + type: 2 + } + }, + { + id: 6, + required: 0, + data: { + type: 3 + } + }, + { + id: 7, + required: 0, + data: { + type: 12 + } + } + ] }, - cta: { - required: false - }, - rating: { - required: false - }, - icon: { - required: true, - sizes: [50, 50] - } + sendTargetingKeys: false, } }, "bids": [{ diff --git a/test/spec/modules/smaatoBidAdapter_spec.js b/test/spec/modules/smaatoBidAdapter_spec.js index 2bbf395d0ae..c7dbf1363e7 100644 --- a/test/spec/modules/smaatoBidAdapter_spec.js +++ b/test/spec/modules/smaatoBidAdapter_spec.js @@ -794,176 +794,116 @@ describe('smaatoBidAdapterTest', () => { }); describe('buildRequests for native imps', () => { - const NATIVE_PREBID_MEDIATYPE = { - image: { - required: true, - sizes: [150, 50], - aspect_ratios: [{ - min_width: 100, - min_height: 25 - }] - }, - icon: { - required: true, - sizes: [50, 50] - }, - title: { - required: true, - len: 80 - }, - sponsoredBy: { - required: true - }, - body: { - required: true - }, - rating: { - required: true - }, - likes: { - required: true - }, - downloads: { - required: true - }, - price: { - required: true - }, - salePrice: { - required: true - }, - phone: { - required: false - }, - address: { - required: true - }, - body2: { - required: false - }, - displayUrl: { - required: false - }, - cta: { - required: true - }, - privacyLink: { - required: false - } - }; - const NATIVE_OPENRTB_IMP = { + const NATIVE_OPENRTB_REQUEST = { ver: '1.2', - privacy: 1, assets: [ { - 'id': 4, - 'required': 1, - 'img': { - 'type': 3, - 'w': 150, - 'h': 50, - 'wmin': 100, - 'hmin': 25 + id: 4, + required: 1, + img: { + type: 3, + w: 150, + h: 50, } }, { - 'id': 2, - 'required': 1, - 'img': { - 'type': 2, - 'w': 50, - 'h': 50 + id: 2, + required: 1, + img: { + type: 2, + w: 50, + h: 50 } }, { - 'id': 0, - 'required': 1, - 'title': { - 'len': 80 + id: 0, + required: 1, + title: { + len: 80 } }, { - 'id': 1, - 'required': 1, - 'data': { - 'type': 1 + id: 1, + required: 1, + data: { + type: 1 } }, { - 'id': 3, - 'required': 1, - 'data': { - 'type': 2 + id: 3, + required: 1, + data: { + type: 2 } }, { - 'id': 5, - 'required': 1, - 'data': { - 'type': 3 + id: 5, + required: 1, + data: { + type: 3 } }, { - 'id': 6, - 'required': 1, - 'data': { - 'type': 4 + id: 6, + required: 1, + data: { + type: 4 } }, { - 'id': 7, - 'required': 1, - 'data': { - 'type': 5 + id: 7, + required: 1, + data: { + type: 5 } }, { - 'id': 8, - 'required': 1, - 'data': { - 'type': 6 + id: 8, + required: 1, + data: { + type: 6 } }, { - 'id': 9, - 'required': 1, - 'data': { - 'type': 7 + id: 9, + required: 1, + data: { + type: 7 } }, { - 'id': 10, - 'required': 0, - 'data': { - 'type': 8 + id: 10, + required: 0, + data: { + type: 8 } }, { - 'id': 11, - 'required': 1, - 'data': { - 'type': 9 + id: 11, + required: 1, + data: { + type: 9 } }, { - 'id': 12, - 'required': 0, - 'data': { - 'type': 10 + id: 12, + require: 0, + data: { + type: 10 } }, { - 'id': 13, - 'required': 0, - 'data': { - 'type': 11 + id: 13, + required: 0, + data: { + type: 11 } }, { - 'id': 14, - 'required': 1, - 'data': { - 'type': 12 + id: 14, + required: 1, + data: { + type: 12 } } ] @@ -975,9 +915,7 @@ describe('smaatoBidAdapterTest', () => { publisherId: 'publisherId', adspaceId: 'adspaceId' }, - mediaTypes: { - native: NATIVE_PREBID_MEDIATYPE - }, + nativeOrtbRequest: NATIVE_OPENRTB_REQUEST, adUnitCode: '/19968336/header-bid-tag-0', transactionId: 'transactionId', bidId: 'bidId', @@ -995,7 +933,25 @@ describe('smaatoBidAdapterTest', () => { expect(req.imp[0].id).to.be.equal('bidId'); expect(req.imp[0].tagid).to.be.equal('adspaceId'); expect(req.imp[0].bidfloor).to.be.undefined; - expect(req.imp[0].native).to.deep.equal(NATIVE_OPENRTB_IMP); + expect(req.imp[0].native.request).to.deep.equal(JSON.stringify(NATIVE_OPENRTB_REQUEST)); + }); + + it('sends bidfloor when configured', () => { + const singleNativeBidRequestWithFloor = Object.assign({}, singleNativeBidRequest); + singleNativeBidRequestWithFloor.getFloor = function(arg) { + if (arg.currency === 'USD' && + arg.mediaType === 'native' && + JSON.stringify(arg.size) === JSON.stringify([150, 50])) { + return { + currency: 'USD', + floor: 0.123 + } + } + } + const reqs = spec.buildRequests([singleNativeBidRequestWithFloor], defaultBidderRequest); + + const req = extractPayloadOfFirstAndOnlyRequest(reqs); + expect(req.imp[0].bidfloor).to.be.equal(0.123); }); }); @@ -1138,6 +1094,82 @@ describe('smaatoBidAdapterTest', () => { } } + const NATIVE_RESPONSE = { + ver: '1.2', + link: { + url: 'https://link.url', + clicktrackers: [ + 'http://click.url/v1/click?e=prebid' + ] + }, + assets: [ + { + id: 0, + required: 1, + title: { + text: 'Title' + } + }, + { + id: 2, + required: 1, + img: { + type: 1, + url: 'https://logo.png', + w: 40, + h: 40 + } + }, + { + id: 4, + required: 1, + img: { + type: 3, + url: 'https://main.png', + w: 480, + h: 320 + } + }, + { + id: 3, + required: 1, + data: { + type: 2, + value: 'Desc' + } + }, + { + id: 14, + required: 1, + data: { + type: 12, + value: 'CTAText' + } + }, + { + id: 5, + required: 0, + data: { + type: 3, + value: '2 stars' + } + } + ], + eventtrackers: [ + { + event: 2, + method: 1, + url: 'https://js.url' + }, + { + event: 1, + method: 1, + url: 'http://view.url/v1/view?e=prebid' + } + ], + privacy: 'https://privacy.com/' + } + const buildOpenRtbBidResponse = (adType) => { let adm = ''; @@ -1185,85 +1217,7 @@ describe('smaatoBidAdapterTest', () => { adm = ''; break; case ADTYPE_NATIVE: - adm = JSON.stringify( - { - 'native': { - 'ver': '1.2', - 'link': { - 'url': 'https://link.url', - 'clicktrackers': [ - 'http://click.url/v1/click?e=prebid' - ] - }, - 'assets': [ - { - 'id': 0, - 'required': 1, - 'title': { - 'text': 'Title' - } - }, - { - 'id': 2, - 'required': 1, - 'img': { - 'type': 1, - 'url': 'https://logo.png', - 'w': 40, - 'h': 40 - } - }, - { - 'id': 4, - 'required': 1, - 'img': { - 'type': 3, - 'url': 'https://main.png', - 'w': 480, - 'h': 320 - } - }, - { - 'id': 3, - 'required': 1, - 'data': { - 'type': 2, - 'value': 'Desc' - } - }, - { - 'id': 14, - 'required': 1, - 'data': { - 'type': 12, - 'value': 'CTAText' - } - }, - { - 'id': 5, - 'required': 0, - 'data': { - 'type': 3, - 'value': '2 stars' - } - } - ], - 'eventtrackers': [ - { - 'event': 2, - 'method': 1, - 'url': 'https://js.url' - }, - { - 'event': 1, - 'method': 1, - 'url': 'http://view.url/v1/view?e=prebid' - } - ], - 'privacy': 'https://privacy.com/' - } - } - ) + adm = JSON.stringify({ native: NATIVE_RESPONSE }) break; default: throw Error('Invalid AdType'); @@ -1400,37 +1354,15 @@ describe('smaatoBidAdapterTest', () => { it('single native response', () => { const bids = spec.interpretResponse(buildOpenRtbBidResponse(ADTYPE_NATIVE), buildBidRequest()); - const nativeResponse = { - 'body': 'Desc', - 'clickTrackers': [ - 'http://click.url/v1/click?e=prebid' - ], - 'clickUrl': 'https://link.url', - 'cta': 'CTAText', - 'icon': { - 'height': 40, - 'url': 'https://logo.png', - 'width': 40 - }, - 'image': { - 'height': 320, - 'url': 'https://main.png', - 'width': 480 - }, - 'impressionTrackers': [ - 'http://view.url/v1/view?e=prebid' - ], - 'privacyLink': 'https://privacy.com/', - 'rating': '2 stars', - 'title': 'Title' - } expect(bids).to.deep.equal([ { requestId: '226416e6e6bf41', cpm: 0.01, width: 350, height: 50, - native: nativeResponse, + native: { + ortb: NATIVE_RESPONSE + }, ttl: 300, creativeId: 'CR69381', dealId: '12345',