From a262b64a6bd70644de9a45ca47f3062e61116691 Mon Sep 17 00:00:00 2001 From: Philip Watson Date: Thu, 8 Jun 2023 03:04:50 +1200 Subject: [PATCH] StroeerCore Bid Adapter: add price floor support (#9962) --- modules/stroeerCoreBidAdapter.js | 61 ++++++- .../modules/stroeerCoreBidAdapter_spec.js | 157 +++++++++++++++++- 2 files changed, 206 insertions(+), 12 deletions(-) diff --git a/modules/stroeerCoreBidAdapter.js b/modules/stroeerCoreBidAdapter.js index e579b1f94de..50f456c29a0 100644 --- a/modules/stroeerCoreBidAdapter.js +++ b/modules/stroeerCoreBidAdapter.js @@ -1,6 +1,7 @@ import {buildUrl, deepAccess, getWindowSelf, getWindowTop, isEmpty, isStr, logWarn} from '../src/utils.js'; import {registerBidder} from '../src/adapters/bidderFactory.js'; import {BANNER, VIDEO} from '../src/mediaTypes.js'; +import {find} from '../src/polyfill.js'; const GVL_ID = 136; const BIDDER_CODE = 'stroeerCore'; @@ -211,12 +212,16 @@ const mapToPayloadBaseBid = (bidRequest) => ({ viz: elementInView(bidRequest.adUnitCode), }); -const mapToPayloadBannerBid = (bidRequest) => ({ - ban: { - siz: deepAccess(bidRequest, 'mediaTypes.banner.sizes') || [], - }, - ...mapToPayloadBaseBid(bidRequest) -}); +const mapToPayloadBannerBid = (bidRequest) => { + const sizes = deepAccess(bidRequest, 'mediaTypes.banner.sizes') || []; + return ({ + ban: { + siz: sizes, + fp: createFloorPriceObject(BANNER, sizes, bidRequest) + }, + ...mapToPayloadBaseBid(bidRequest) + }); +}; const mapToPayloadVideoBid = (bidRequest) => { const video = deepAccess(bidRequest, 'mediaTypes.video') || {}; @@ -225,9 +230,53 @@ const mapToPayloadVideoBid = (bidRequest) => { ctx: video.context, siz: video.playerSize, mim: video.mimes, + fp: createFloorPriceObject(VIDEO, [video.playerSize], bidRequest), }, ...mapToPayloadBaseBid(bidRequest) }; }; +const createFloorPriceObject = (mediaType, sizes, bidRequest) => { + if (!bidRequest.getFloor) { + return undefined; + } + + const defaultFloor = bidRequest.getFloor({ + currency: 'EUR', + mediaType: mediaType, + size: '*' + }); + + const sizeFloors = sizes.map(size => { + const floor = bidRequest.getFloor({ + currency: 'EUR', + mediaType: mediaType, + size: [size[0], size[1]] + }); + return {...floor, size}; + }); + + const floorWithCurrency = find([defaultFloor].concat(sizeFloors), floor => floor.currency); + + if (!floorWithCurrency) { + return undefined; + } + + const currency = floorWithCurrency.currency; + const defaultFloorPrice = defaultFloor.currency === currency ? defaultFloor.floor : undefined; + + return { + def: defaultFloorPrice, + cur: currency, + siz: sizeFloors + .filter(sizeFloor => sizeFloor.currency === currency) + .filter(sizeFloor => sizeFloor.floor !== defaultFloorPrice) + .map(sizeFloor => ({ + w: sizeFloor.size[0], + h: sizeFloor.size[1], + p: sizeFloor.floor + })) + }; +} + registerBidder(spec); diff --git a/test/spec/modules/stroeerCoreBidAdapter_spec.js b/test/spec/modules/stroeerCoreBidAdapter_spec.js index c2806317ee6..55d79804a38 100644 --- a/test/spec/modules/stroeerCoreBidAdapter_spec.js +++ b/test/spec/modules/stroeerCoreBidAdapter_spec.js @@ -527,14 +527,16 @@ describe('stroeerCore bid adapter', function () { 'bid': 'bid8', 'viz': true, 'ban': { - 'siz': [[300, 600], [160, 60]] - } + 'siz': [[300, 600], [160, 60]], + 'fp': undefined + }, }, { 'sid': 'ABC=', 'bid': 'bid12', 'ban': { - 'siz': [[100, 200], [300, 500]] + 'siz': [[100, 200], [300, 500]], + 'fp': undefined }, 'viz': undefined } @@ -548,7 +550,8 @@ describe('stroeerCore bid adapter', function () { 'vid': { 'ctx': 'instream', 'siz': [640, 480], - 'mim': ['video/mp4', 'video/quicktime'] + 'mim': ['video/mp4', 'video/quicktime'], + 'fp': undefined } } ]; @@ -589,7 +592,8 @@ describe('stroeerCore bid adapter', function () { 'bid': 'bid3', 'viz': true, 'ban': { - 'siz': [[100, 200], [300, 500]] + 'siz': [[100, 200], [300, 500]], + 'fp': undefined } } ]; @@ -602,7 +606,8 @@ describe('stroeerCore bid adapter', function () { 'vid': { 'ctx': 'instream', 'siz': [640, 480], - 'mim': ['video/mp4', 'video/quicktime'] + 'mim': ['video/mp4', 'video/quicktime'], + 'fp': undefined } } ]; @@ -696,6 +701,146 @@ describe('stroeerCore bid adapter', function () { const serverRequestInfo = spec.buildRequests(bidReq.bids, bidReq); assert.deepEqual(serverRequestInfo.data.schain, schain); }); + + it('should add floor info to banner bid request if floor is available', () => { + const bidReq = buildBidderRequest(); + + const getFloorStub1 = sinon.stub(); + const getFloorStub2 = sinon.stub(); + + getFloorStub1 + .returns({}) + .withArgs({currency: 'EUR', mediaType: BANNER, size: '*'}) + .returns({currency: 'TRY', floor: 0.7}) + .withArgs({currency: 'EUR', mediaType: 'banner', size: [300, 600]}) + .returns({currency: 'TRY', floor: 1.3}) + .withArgs({currency: 'EUR', mediaType: 'banner', size: [160, 60]}) + .returns({currency: 'TRY', floor: 2.5}) + + getFloorStub2 + .returns({}) + .withArgs({currency: 'EUR', mediaType: 'banner', size: '*'}) + .returns({currency: 'USD', floor: 1.2}) + .withArgs({currency: 'EUR', mediaType: 'banner', size: [728, 90]}) + .returns({currency: 'USD', floor: 1.85}) + + bidReq.bids[0].getFloor = getFloorStub1; + bidReq.bids[1].getFloor = getFloorStub2; + + const serverRequestInfo = spec.buildRequests(bidReq.bids, bidReq); + + const serverRequestBids = serverRequestInfo.data.bids; + const firstBid = serverRequestBids[0]; + const secondBid = serverRequestBids[1]; + + assert.nestedPropertyVal(firstBid, 'ban.fp.def', 0.7); + assert.nestedPropertyVal(firstBid, 'ban.fp.cur', 'TRY'); + assert.deepNestedPropertyVal(firstBid, 'ban.fp.siz', [{w: 300, h: 600, p: 1.3}, {w: 160, h: 60, p: 2.5}]); + + assert.isTrue(getFloorStub1.calledThrice); + + assert.nestedPropertyVal(secondBid, 'ban.fp.def', 1.2); + assert.nestedPropertyVal(secondBid, 'ban.fp.cur', 'USD'); + assert.deepNestedPropertyVal(secondBid, 'ban.fp.siz', [{w: 728, h: 90, p: 1.85}]); + + assert.isTrue(getFloorStub2.calledTwice); + }); + + it('should add floor info to video bid request if floor is available', () => { + const bidReq = buildBidderRequest(); + + const getFloorStub1 = sinon.stub(); + const getFloorStub2 = sinon.stub(); + + getFloorStub1 + .returns({}) + .withArgs({currency: 'EUR', mediaType: 'video', size: '*'}) + .returns({currency: 'NZD', floor: 3.25}) + .withArgs({currency: 'EUR', mediaType: 'video', size: [640, 480]}) + .returns({currency: 'NZD', floor: 4.10}); + + getFloorStub2 + .returns({}) + .withArgs({currency: 'EUR', mediaType: 'video', size: '*'}) + .returns({currency: 'GBP', floor: 4.75}) + .withArgs({currency: 'EUR', mediaType: 'video', size: [1280, 720]}) + .returns({currency: 'GBP', floor: 6.50}) + + delete bidReq.bids[0].mediaTypes.banner; + bidReq.bids[0].mediaTypes.video = { + playerSize: [640, 480], + context: 'instream' + }; + + delete bidReq.bids[1].mediaTypes.banner; + bidReq.bids[1].mediaTypes.video = { + playerSize: [1280, 720], + context: 'outstream' + }; + + bidReq.bids[0].getFloor = getFloorStub1; + bidReq.bids[1].getFloor = getFloorStub2; + + const serverRequestInfo = spec.buildRequests(bidReq.bids, bidReq); + + const serverRequestBids = serverRequestInfo.data.bids; + const firstBid = serverRequestBids[0]; + const secondBid = serverRequestBids[1]; + + assert.nestedPropertyVal(firstBid, 'vid.fp.def', 3.25); + assert.nestedPropertyVal(firstBid, 'vid.fp.cur', 'NZD'); + assert.deepNestedPropertyVal(firstBid, 'vid.fp.siz', [{w: 640, h: 480, p: 4.10}]); + + assert.isTrue(getFloorStub1.calledTwice); + + assert.nestedPropertyVal(secondBid, 'vid.fp.def', 4.75); + assert.nestedPropertyVal(secondBid, 'vid.fp.cur', 'GBP'); + assert.deepNestedPropertyVal(secondBid, 'vid.fp.siz', [{w: 1280, h: 720, p: 6.50}]); + + assert.isTrue(getFloorStub2.calledTwice); + }); + + it('should not add floor info to bid request if floor is unavailable', () => { + const bidReq = buildBidderRequest(); + const getFloorSpy = sinon.spy(() => ({})); + + delete bidReq.bids[0].getFloor; + bidReq.bids[1].getFloor = getFloorSpy; + + const serverRequestInfo = spec.buildRequests(bidReq.bids, bidReq); + + const serverRequestBids = serverRequestInfo.data.bids; + const firstBid = serverRequestBids[0]; + const secondBid = serverRequestBids[1]; + + assert.nestedPropertyVal(firstBid, 'ban.fp', undefined); + assert.nestedPropertyVal(secondBid, 'ban.fp', undefined); + + assert.isTrue(getFloorSpy.calledWith({currency: 'EUR', mediaType: 'banner', size: '*'})); + assert.isTrue(getFloorSpy.calledWith({currency: 'EUR', mediaType: 'banner', size: [728, 90]})); + assert.isTrue(getFloorSpy.calledTwice); + }); + + it('should not add floor info for a size when it is the same as the default', () => { + const bidReq = buildBidderRequest(); + const getFloorStub = sinon.stub(); + + getFloorStub + .returns({currency: 'EUR', floor: 1.9}) + .withArgs({currency: 'EUR', mediaType: BANNER, size: [160, 60]}) + .returns({currency: 'EUR', floor: 2.7}); + + bidReq.bids[0].getFloor = getFloorStub; + + const serverRequestInfo = spec.buildRequests(bidReq.bids, bidReq); + + const serverRequestBids = serverRequestInfo.data.bids; + const bid = serverRequestBids[0]; + + assert.nestedPropertyVal(bid, 'ban.fp.def', 1.9); + assert.nestedPropertyVal(bid, 'ban.fp.cur', 'EUR'); + assert.deepNestedPropertyVal(bid, 'ban.fp.siz', [{w: 160, h: 60, p: 2.7}]); + }); }); }); });