-
Notifications
You must be signed in to change notification settings - Fork 2.1k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
BMS Bid Adapter : initial release (#12621)
* wip * chore: update ENDPOINT_URL * chore: update permission for localstorage * feat(bmsBidAdapter): implement bid floor logic and update request structure * test(bmsBidAdapter): remove commented-out tests for interpretResponse * wip * wip * Refactor geolocation implementationn * chore: minor adjustments * feat: add bidWon * update test * chore: Change double quotes to single quotes * Update creativeId and creative_id values * refactor: remove unused cookie ID handling from bid request * wip * Remove deprecated BMS sample HTML and update BMS bid adapter to use JSON.stringify for requests and sendBeacon for bid won notifications * wip * Update creative.html * Update creative.html --------- Co-authored-by: Patrick McCann <[email protected]>
- Loading branch information
1 parent
f86d267
commit 9079ca8
Showing
3 changed files
with
371 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,158 @@ | ||
import { ortbConverter } from '../libraries/ortbConverter/converter.js'; | ||
import { registerBidder } from '../src/adapters/bidderFactory.js'; | ||
import { sendBeacon } from '../src/ajax.js'; | ||
import { BANNER } from '../src/mediaTypes.js'; | ||
import { getStorageManager } from '../src/storageManager.js'; | ||
import { | ||
replaceAuctionPrice, | ||
isFn, | ||
isPlainObject, | ||
deepSetValue, | ||
isEmpty, | ||
} from '../src/utils.js'; | ||
const BIDDER_CODE = 'bms'; | ||
const ENDPOINT_URL = | ||
'https://api.prebid.int.us-east-1.bluems.com/v1/bid?exchangeId=prebid'; | ||
const GVLID = 1105; | ||
const DEFAULT_CURRENCY = 'USD'; | ||
const DEFAULT_BID_TTL = 1200; | ||
|
||
export const storage = getStorageManager({ bidderCode: BIDDER_CODE }); | ||
|
||
function getBidFloor(bid) { | ||
if (isFn(bid.getFloor)) { | ||
let floor = bid.getFloor({ | ||
currency: DEFAULT_CURRENCY, | ||
mediaType: BANNER, | ||
size: '*', | ||
}); | ||
if ( | ||
isPlainObject(floor) && | ||
!isNaN(floor.floor) && | ||
floor.currency === DEFAULT_CURRENCY | ||
) { | ||
return floor.floor; | ||
} | ||
} | ||
return null; | ||
} | ||
|
||
const converter = ortbConverter({ | ||
context: { | ||
netRevenue: true, // Default net revenue configuration | ||
ttl: 100, // Default time-to-live for bid responses | ||
}, | ||
imp, | ||
request, | ||
}); | ||
|
||
function request(buildRequest, imps, bidderRequest, context) { | ||
let request = buildRequest(imps, bidderRequest, context); | ||
|
||
// Add publisher ID | ||
deepSetValue(request, 'site.publisher.id', context.publisherId); | ||
return request; | ||
} | ||
|
||
function imp(buildImp, bidRequest, context) { | ||
let imp = buildImp(bidRequest, context); | ||
const floor = getBidFloor(bidRequest); | ||
imp.tagid = bidRequest.params.placementId; | ||
|
||
if (floor) { | ||
imp.bidfloor = floor; | ||
imp.bidfloorcur = DEFAULT_CURRENCY; | ||
} | ||
|
||
return imp; | ||
} | ||
|
||
export const spec = { | ||
code: BIDDER_CODE, | ||
gvlid: GVLID, | ||
supportedMediaTypes: [BANNER], | ||
|
||
// Validate bid request | ||
isBidRequestValid: function (bid) { | ||
return !!bid.params.placementId && !!bid.params.publisherId; | ||
}, | ||
|
||
// Build OpenRTB requests using `ortbConverter` | ||
buildRequests: function (validBidRequests, bidderRequest) { | ||
const context = { | ||
publisherId: validBidRequests.find( | ||
(bidRequest) => bidRequest.params?.publisherId | ||
)?.params.publisherId, | ||
}; | ||
|
||
const ortbRequest = converter.toORTB({ | ||
bidRequests: validBidRequests, | ||
bidderRequest, | ||
context, | ||
}); | ||
|
||
// Add extensions to the request | ||
ortbRequest.ext = ortbRequest.ext || {}; | ||
deepSetValue(ortbRequest, 'ext.gvlid', GVLID); | ||
|
||
return [ | ||
{ | ||
method: 'POST', | ||
url: ENDPOINT_URL, | ||
data: JSON.stringify(ortbRequest), | ||
options: { | ||
contentType: 'text/plain', | ||
withCredentials: false, | ||
}, | ||
}, | ||
]; | ||
}, | ||
|
||
interpretResponse: (serverResponse) => { | ||
if (!serverResponse || isEmpty(serverResponse.body)) return []; | ||
|
||
let bids = []; | ||
serverResponse.body.seatbid.forEach((response) => { | ||
response.bid.forEach((bid) => { | ||
const mediaType = bid.ext?.mediaType || 'banner'; | ||
bids.push({ | ||
ad: replaceAuctionPrice(bid.adm, bid.price), | ||
adapterCode: BIDDER_CODE, | ||
cpm: bid.price, | ||
creativeId: bid.ext.bms.adId, | ||
currency: serverResponse.body.cur || 'USD', | ||
deferBilling: false, | ||
deferRendering: false, | ||
width: bid.w, | ||
height: bid.h, | ||
mediaType, | ||
netRevenue: true, | ||
originalCpm: bid.price, | ||
originalCurrency: serverResponse.body.cur || 'USD', | ||
requestId: bid.impid, | ||
seatBidId: bid.id, | ||
ttl: typeof bid.exp === 'number' ? bid.exp : DEFAULT_BID_TTL, | ||
meta: { | ||
advertiserDomains: bid.adomain || [], | ||
networkId: bid.ext?.networkId || 1105, | ||
networkName: bid.ext?.networkName || 'BMS', | ||
} | ||
}); | ||
}); | ||
}); | ||
return bids; | ||
}, | ||
|
||
onBidWon: function (bid) { | ||
const { burl, nurl } = bid || {}; | ||
if (nurl) { | ||
sendBeacon(nurl); | ||
} | ||
|
||
if (burl) { | ||
sendBeacon(burl); | ||
} | ||
}, | ||
}; | ||
|
||
registerBidder(spec); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,48 @@ | ||
# Overview | ||
|
||
Module Name: bms Bidder Adapter | ||
Module Type: Bidder Adapter | ||
Maintainer: [email protected] | ||
|
||
# Description | ||
|
||
Module that connects to bms's demand sources. | ||
|
||
# Test Parameters | ||
|
||
``` | ||
var adUnits = [ | ||
{ | ||
code: "test-div", | ||
mediaTypes: { | ||
banner: { | ||
sizes: [ | ||
[300, 250], | ||
[300, 600], | ||
], | ||
}, | ||
}, | ||
floors: { | ||
currency: "USD", | ||
schema: { | ||
delimiter: "|", | ||
fields: ["mediaType", "size"], | ||
}, | ||
values: { | ||
"banner|300x250": 1.1, | ||
"banner|300x600": 1.35, | ||
"banner|*": 2, | ||
}, | ||
}, | ||
bids: [ | ||
{ | ||
bidder: "bms", | ||
params: { | ||
placementId: 13144370, | ||
publisherId: 13144370, | ||
}, | ||
}, | ||
], | ||
}, | ||
]; | ||
``` |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,165 @@ | ||
import { expect } from 'chai'; | ||
import sinon from 'sinon'; | ||
import { spec, storage } from 'modules/bmsBidAdapter.js'; | ||
|
||
const BIDDER_CODE = 'bms'; | ||
const ENDPOINT_URL = | ||
'https://api.prebid.int.us-east-1.bluems.com/v1/bid?exchangeId=prebid'; | ||
const GVLID = 1105; | ||
const CURRENCY = 'USD'; | ||
|
||
describe('bmsBidAdapter:', function () { | ||
let sandbox; | ||
|
||
beforeEach(function () { | ||
sandbox = sinon.createSandbox(); | ||
}); | ||
|
||
afterEach(function () { | ||
sandbox.restore(); | ||
}); | ||
|
||
describe('isBidRequestValid:', function () { | ||
it('should return true for valid bid requests', function () { | ||
const validBid = { | ||
params: { | ||
placementId: '12345', | ||
publisherId: '67890', | ||
}, | ||
}; | ||
expect(spec.isBidRequestValid(validBid)).to.be.true; | ||
}); | ||
|
||
it('should return false for invalid bid requests', function () { | ||
const invalidBid = { | ||
params: { | ||
placementId: '12345', | ||
}, | ||
}; | ||
expect(spec.isBidRequestValid(invalidBid)).to.be.false; | ||
}); | ||
}); | ||
|
||
describe('buildRequests:', function () { | ||
let validBidRequests; | ||
let bidderRequest; | ||
|
||
beforeEach(function () { | ||
validBidRequests = [ | ||
{ | ||
bidId: 'bid1', | ||
params: { | ||
placementId: '12345', | ||
publisherId: '67890', | ||
}, | ||
getFloor: () => ({ currency: CURRENCY, floor: 1.5 }), | ||
}, | ||
]; | ||
|
||
bidderRequest = { | ||
refererInfo: { | ||
page: 'https://example.com', | ||
}, | ||
}; | ||
|
||
sandbox.stub(storage, 'getDataFromLocalStorage').returns('testBuyerId'); | ||
}); | ||
|
||
it('should build a valid OpenRTB request', function () { | ||
const [request] = spec.buildRequests(validBidRequests, bidderRequest); | ||
expect(request.method).to.equal('POST'); | ||
expect(request.url).to.equal(ENDPOINT_URL); | ||
expect(request.options.contentType).to.equal('text/plain'); | ||
const ortbRequest = JSON.parse(request.data); | ||
|
||
expect(ortbRequest.ext.gvlid).to.equal(GVLID); | ||
expect(ortbRequest.imp[0].bidfloor).to.equal(1.5); | ||
expect(ortbRequest.imp[0].bidfloorcur).to.equal(CURRENCY); | ||
}); | ||
|
||
it('should omit bidfloor if getFloor is not implemented', function () { | ||
validBidRequests[0].getFloor = undefined; | ||
|
||
const [request] = spec.buildRequests(validBidRequests, bidderRequest); | ||
const ortbRequest = JSON.parse(request.data); | ||
|
||
expect(ortbRequest.imp[0].bidfloor).to.be.undefined; | ||
}); | ||
|
||
it('should convert from fromORTB', function () { | ||
const response = { | ||
id: 'response-id-123456', | ||
cur: 'USD', | ||
bidid: '2rgRKcbHfDyX6ZU4zuPuf38h000', | ||
seatbid: [ | ||
{ | ||
bid: [ | ||
{ | ||
id: '2rgRKcbHfDyX6ZU4zuPuf521444:0', | ||
impid: '3b948a96652621', | ||
price: 2, | ||
adomain: ['example.com'], | ||
adid: '0', | ||
adm: '<iframe src="https://ads.example.com/ad-content?acc=account-123&ad=ad-123&bid=bid-id-abcdef:0&imp=impression-id-12345" height="600" width="300" marginwidth="0" marginheight="0" align="top" scrolling="No" frameborder="0" hspace="0" vspace="0"></iframe>', | ||
iurl: 'https://ads.bluemsusercontent.com/v1/ad-container?acc=306850905425&ad=2pMGbaJioMDwMIESvwlCUekrdNA', | ||
h: 600, | ||
w: 300, | ||
nurl: 'https://bid-notice.rtb.bluems.com/v1/bid:won?winPrice=${AUCTION_PRICE}&accountId=306850905425&adId=2pMGbaJioMDwMIESvwlCUekrdNA&campaignId=2Xzb0pyfcOibtp9A5XsUSkzj2IT&exchangeId=prebid&tagId=13144370&impressionId=3b948a96652621&bidId=2rgRKcbHfDyX6ZU4zuPuf38hDB8%3A0&bidPrice=2&bidFloor=2&height=600&width=300®ion=us-east-1&targetId=2rgH24MVckzSou4IpTyUa5l3Ija¤cy=USD&campaignCurrency=USD&campaignCurrencyConversionFactor=1&publisherId=13144370&domain=d3fef36u6k6muh.cloudfront.net', | ||
lurl: 'https://bid-notice.rtb.bluems.com/v1/bid:lost?winPrice=${AUCTION_PRICE}&marketBidRatio=${AUCTION_MBR}&lossReasonCode=${AUCTION_LOSS}&accountId=306850905425&adId=2pMGbaJioMDwMIESvwlCUekrdNA&campaignId=2Xzb0pyfcOibtp9A5XsUSkzj2IT&exchangeId=prebid&tagId=13144370&impressionId=3b948a96652621&bidId=2rgRKcbHfDyX6ZU4zuPuf38hDB8%3A0&bidPrice=2&bidFloor=2&height=600&width=300®ion=us-east-1&targetId=2rgH24MVckzSou4IpTyUa5l3Ija¤cy=USD&campaignCurrency=USD&campaignCurrencyConversionFactor=1&publisherId=13144370&domain=d3fef36u6k6muh.cloudfront.net', | ||
burl: 'https://bid-notice.rtb.bluems.com/v1/bid:charged?winPrice=${AUCTION_PRICE}&accountId=306850905425&adId=2pMGbaJioMDwMIESvwlCUekrdNA&campaignId=2Xzb0pyfcOibtp9A5XsUSkzj2IT&exchangeId=prebid&tagId=13144370&impressionId=3b948a96652621&bidId=2rgRKcbHfDyX6ZU4zuPuf38hDB8%3A0&bidPrice=2&bidFloor=2&height=600&width=300®ion=us-east-1&targetId=2rgH24MVckzSou4IpTyUa5l3Ija¤cy=USD&campaignCurrency=USD&campaignCurrencyConversionFactor=1&publisherId=13144370&domain=d3fef36u6k6muh.cloudfront.net', | ||
exp: 60, | ||
ext: { | ||
bms: { | ||
accountId: '306850900000', | ||
campaignId: '2Xzb0pyfcOibtp9A5X8546254528', | ||
adId: '2pMGbaJioMDwMIESvwlCUlkojug', | ||
region: 'us-east-1', | ||
targetId: '2rgH24MVckzSou4IpTyUalakush', | ||
}, | ||
}, | ||
}, | ||
], | ||
seat: '1', | ||
}, | ||
], | ||
}; | ||
const request = { | ||
id: '10bb57ee-712f-43e9-9769-b26d03lklkih', | ||
bidder: BIDDER_CODE, | ||
params: { | ||
source: 886409, | ||
}, | ||
mediaTypes: { | ||
banner: { | ||
sizes: [ | ||
[300, 250], | ||
[300, 600], | ||
], | ||
}, | ||
}, | ||
adUnitCode: 'div-gpt-ad-1460505iosakju-0', | ||
transactionId: '7d79850b-70aa-4c0f-af95-c1d524sskjkjh', | ||
sizes: [ | ||
[300, 250], | ||
[300, 600], | ||
], | ||
bidId: '2eb89f0f062afe', | ||
bidderRequestId: '1ae6c8e18f8462', | ||
auctionId: '1286637c-51bc-4fdd-8e35-2435elklklka', | ||
ortb2: {}, | ||
}; | ||
|
||
const [ortbReq] = spec.buildRequests([request], { | ||
bids: [request], | ||
}); | ||
|
||
const ortbResponse = spec.interpretResponse( | ||
{ body: response }, | ||
{ data: ortbReq.data } | ||
); | ||
|
||
expect(ortbResponse.length).to.eq(1); | ||
expect(ortbResponse[0].mediaType).to.eq('banner'); | ||
}); | ||
}); | ||
}); |