-
Notifications
You must be signed in to change notification settings - Fork 2.1k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
RelevateHealth Bid Adapter : Initial release #11640
Changes from 1 commit
7b358f2
0464942
190f791
5dfb542
54ef288
3d7ecce
233aafa
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,151 @@ | ||
import { registerBidder } from '../src/adapters/bidderFactory.js'; | ||
import { BANNER } from '../src/mediaTypes.js'; | ||
import { deepAccess, generateUUID, isArray, logError } from '../src/utils.js'; | ||
|
||
const BIDDER_CODE = 'relevatehealth'; | ||
|
||
const ENDPOINT_URL = 'https://rtb.relevate.health/prebid/relevate'; | ||
|
||
function buildRequests(bidRequests, bidderRequest) { | ||
const requests = []; | ||
// Loop through each bid request | ||
bidRequests.forEach(bid => { | ||
// Construct the bid request object | ||
const request = { | ||
id: generateUUID(), | ||
placementId: bid.params.placementId, | ||
imp: [{ | ||
id: bid.bidId, | ||
banner: getBanner(bid), | ||
bidfloor: bid.params.bid_floor | ||
}], | ||
site: getSite(bidderRequest), | ||
user: buildUser(bid) | ||
}; | ||
|
||
// Get uspConsent from bidderRequest | ||
if (bidderRequest && bidderRequest.uspConsent) { | ||
request.us_privacy = bidderRequest.uspConsent; | ||
} | ||
// Get GPP Consent from bidderRequest | ||
if (bidderRequest?.gppConsent?.gppString) { | ||
request.gpp = bidderRequest.gppConsent.gppString; | ||
request.gpp_sid = bidderRequest.gppConsent.applicableSections; | ||
} else if (bidderRequest?.ortb2?.regs?.gpp) { | ||
request.gpp = bidderRequest.ortb2.regs.gpp; | ||
request.gpp_sid = bidderRequest.ortb2.regs.gpp_sid; | ||
} | ||
|
||
// Get coppa compliance from bidderRequest | ||
if (bidderRequest?.ortb2?.regs?.coppa) { | ||
request.coppa = 1; | ||
} | ||
// Push the constructed bid request to the requests array | ||
requests.push(request); | ||
}); | ||
// Return the array of bid requests | ||
return { | ||
method: 'POST', | ||
url: ENDPOINT_URL, | ||
data: JSON.stringify(requests), | ||
options: { | ||
contentType: 'application/json', | ||
} | ||
}; | ||
} | ||
|
||
// Format the response as per the standards | ||
function interpretResponse(bidResponse, bidRequest) { | ||
let resp = []; | ||
if (bidResponse && bidResponse.body) { | ||
try { | ||
let bids = bidResponse.body.seatbid && bidResponse.body.seatbid[0] ? bidResponse.body.seatbid[0].bid : []; | ||
if (bids) { | ||
bids.forEach(bidObj => { | ||
let newBid = formatResponse(bidObj); | ||
newBid.mediaType = BANNER; | ||
resp.push(newBid); | ||
}); | ||
} | ||
} catch (err) { | ||
logError(err); | ||
} | ||
} | ||
return resp; | ||
} | ||
|
||
// Function to check if Bid is valid | ||
function isBidRequestValid(bid) { | ||
return !!(bid.params.placementId && bid.params.user_id); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Good chance the answer is yes, but did you intend to have placementId be camel case while user_id uses underscores? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. To make all consistent, changed placementId to placement_id. Also changed in Docs |
||
} | ||
|
||
// Function to get banner details | ||
function getBanner(bid) { | ||
if (deepAccess(bid, 'mediaTypes.banner')) { | ||
// Fetch width and height from MediaTypes object, if not provided in bid params | ||
if (deepAccess(bid, 'mediaTypes.banner.sizes') && !bid.params.height && !bid.params.width) { | ||
let sizes = deepAccess(bid, 'mediaTypes.banner.sizes'); | ||
if (isArray(sizes) && sizes.length > 0) { | ||
return { | ||
h: sizes[0][1], | ||
w: sizes[0][0] | ||
} | ||
} | ||
} else { | ||
return { | ||
h: bid.params.height, | ||
w: bid.params.width | ||
} | ||
} | ||
} | ||
} | ||
|
||
// Function to get site details | ||
function getSite(bidderRequest) { | ||
let site = {}; | ||
if (bidderRequest && bidderRequest.refererInfo && bidderRequest.refererInfo.page) { | ||
site.name = bidderRequest.refererInfo.domain; | ||
} else { | ||
site.name = ''; | ||
} | ||
return site; | ||
} | ||
|
||
function formatResponse(bid) { | ||
return { | ||
requestId: bid && bid.impid ? bid.impid : undefined, | ||
cpm: bid && bid.price ? bid.price : 0.0, | ||
width: bid && bid.w ? bid.w : 0, | ||
height: bid && bid.h ? bid.h : 0, | ||
ad: bid && bid.adm ? bid.adm : '', | ||
meta: { | ||
advertiserDomains: bid && bid.adomain ? bid.adomain : [] | ||
}, | ||
creativeId: bid && bid.crid ? bid.crid : undefined, | ||
netRevenue: false, | ||
currency: bid && bid.cur ? bid.cur : 'USD', | ||
ttl: 300, | ||
dealId: bid && bid.dealId ? bid.dealId : undefined | ||
} | ||
} | ||
|
||
function buildUser(bid) { | ||
if (bid && bid.params) { | ||
return { | ||
id: bid.params.user_id && typeof bid.params.user_id == 'string' ? bid.params.user_id : '', | ||
buyeruid: localStorage.getItem('adx_profile_guid') ? localStorage.getItem('adx_profile_guid') : '', | ||
keywords: bid.params.keywords && typeof bid.params.keywords == 'string' ? bid.params.keywords : '', | ||
customdata: bid.params.customdata && typeof bid.params.customdata == 'string' ? bid.params.customdata : '' | ||
} | ||
} | ||
} | ||
|
||
export const spec = { | ||
code: BIDDER_CODE, | ||
supportedMediaTypes: BANNER, | ||
isBidRequestValid, | ||
buildRequests, | ||
interpretResponse | ||
} | ||
|
||
registerBidder(spec); |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,40 @@ | ||
# Overview | ||
|
||
``` | ||
Module Name: relevatehealth Bidder Adapter | ||
Module Type: Bidder Adapter | ||
Maintainer: [email protected] | ||
``` | ||
|
||
# Description | ||
|
||
relevatehealth currently supports the BANNER type ads through prebid js | ||
|
||
Module that connects to relevatehealth's demand sources. | ||
|
||
# Banner Test Request | ||
``` | ||
var adUnits = [ | ||
{ | ||
code: 'banner-ad', | ||
mediaTypes: { | ||
banner: { | ||
sizes: [[160, 600]], | ||
} | ||
} | ||
bids: [ | ||
{ | ||
bidder: 'relevatehealth', | ||
params: { | ||
placementId: 110011, // Required parameter | ||
user_id: '1111111' // Required parameter | ||
width: 160, // Optional parameter | ||
height: 600, // Optional parameter | ||
domain: '', // Optional parameter | ||
bid_floor: 0.5 // Optional parameter | ||
} | ||
} | ||
] | ||
} | ||
]; | ||
``` |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,204 @@ | ||
import { expect } from 'chai'; | ||
import { spec } from '../../../modules/relevatehealthBidAdapter.js'; | ||
import * as utils from '../../../src/utils.js'; | ||
|
||
describe('relevatehealth adapter', function () { | ||
let request; | ||
let bannerResponse, invalidResponse; | ||
|
||
beforeEach(function () { | ||
request = [ | ||
{ | ||
bidder: 'relevatehealth', | ||
mediaTypes: { | ||
banner: { | ||
sizes: [[160, 600]] | ||
} | ||
}, | ||
params: { | ||
placementId: 110011, | ||
user_id: '11211', | ||
width: 160, | ||
height: 600, | ||
domain: '', | ||
bid_floor: 0.5 | ||
} | ||
} | ||
]; | ||
bannerResponse = { | ||
'body': { | ||
'id': 'a48b79e7-7104-4213-99f3-55f3234f2e54', | ||
'seatbid': [{ | ||
'bid': [{ | ||
'id': '3d7dd6dc-7665-4cdc-96a4-5ea192df32b8', | ||
'impid': '285b9c53b2c662', | ||
'price': 20.68, | ||
'adid': '3389', | ||
'adm': "<a href=\"https://r.relevate.health/adx-rtb-m/servlet/WebF_AdManager.AdLinkManager?qs=H4sIAAAAAAAAAx2S2Q1FIQhEW2ITsBwW6b+Ex32J8cMAM2fQGDvalAl1atTnsOaZRLWjokdsKrG8J5i7Mg+1S1tAZ3gH1/SxuACgSLd3Gjid77Ei77uXH1rrFUZLH3KYaopzyPCpQAkqigyfg3QecCaeG/fN4FXT92A6coszhUuer1QfYNJjXmkFStJ6TngOTdsVKUi20+yyxolDMEb6ZIlP6N6OOJ3v1dxHdKtUvmlg+XI1kQAuTDqIrPzNk7RxQKyw8HIZI7560o1LVCM3yDt5czy5YlfdOAy3VewlaEVVXgRA3HOR1c+7HKG0cQC+OBJxuo8P8nJsYqK9Ipk6UY9uagagmJQ+bm6YtX4CSdPs8cdcu7buus/rHiOthp2JRqJQtjQk6xZxe0r4Bg8WnGwGM8m7G8Abs0j5/mly31F0vpshYybsigk3IGU/vpDtT/2mAdV2KjI0l1VnC3+lcF7cyc5FNjXjN+Uop7UFupaj6OlIcVG+Fq16ybcr1l1HZGEUerx9SteCLRV/4Kg53fQE08jpvSu0qa8Dr+K8qz1QTrg+c3+2m5wLHjJhYjGkRpSKtjkIgXXIru6Q+/qDBMMVI/0BzbAHIiADAAA=\" target=\"_blank\"><img src=\"https://cdn.relevate.health/2_310042_1.png\" height=\"600\" width=\"160\"></img></a><img width=\"1px\" height=\"1px\" style=\"display:none;\" src=\"http://rtb.relevate.health:9001/beacon?uid=7a89e67afcc50dd00df1f36b1e113f9e&cc=410014&fccap=3&nid=2\"></img><script async src='https://i.relevate.health/adx-rtb-m/servlet/WebF_AdManager.ImpTracker?qs=&price=${AUCTION_PRICE}%26id%3D110011%2C12517%2C410014%2C310042%2C210009%2C6%2C2%2C12518%2C2%2C12518%2C1%26cb%3D1717164048%26ap%3D1.88000%26mf%3D0.06000%26ai%3D%2C-1%2C-1%2C-1%26ag%3D%5Badx_guid%5D%2Cb52c3caf-261b-45b1-8f6a-12507b95c335%2C123456%2Cb52c3caf-261b-45b1-8f6a-12507b95c335%2C12518_%26as%3D-1%2C-1%26mm%3D-1%2C-1%26ua%3DUnKnown%26ref%3D'></script><script async src='https://i.relevate.health/adx-rtb-m/servlet/WebF_AdManager.ImpCounter?qs=&price=${AUCTION_PRICE}%26id%3D110011%2C12517%2C410014%2C310042%2C210009%2C6%2C2%2C12518%2C2%2C12518%2C1%26cb%3D1717164048%26ap%3D1.88000%26mf%3D0.06000%26ai%3D%2C-1%2C-1%2C-1%26ag%3D%5Badx_guid%5D%2Cb52c3caf-261b-45b1-8f6a-12507b95c335%2C123456%2Cb52c3caf-261b-45b1-8f6a-12507b95c335%2C12518_%26as%3D-1%2C-1%26mm%3D-1%2C-1%26ua%3DUnKnown%26ref%3D'></script>", | ||
'adomain': ['google.com'], | ||
'iurl': 'https://rtb.relevate.health/prebid/relevate', | ||
'cid': '1431/3389', | ||
'crid': '3389', | ||
'w': 160, | ||
'h': 600, | ||
'cat': ['IAB1-15'] | ||
}], | ||
'seat': '00001', | ||
'group': 0 | ||
}], | ||
'cur': 'USD', | ||
'bidid': 'BIDDER_1276' | ||
} | ||
}; | ||
invalidResponse = { | ||
'body': { | ||
'id': 'a48b79e7-7104-4213-99f3-55f3234f2e54', | ||
'seatbid': [{ | ||
'bid': [{ | ||
'id': '3d7dd6dc-7665-4cdc-96a4-5ea192df32b8', | ||
'impid': '285b9c53b2c662', | ||
'price': 20.68, | ||
'adid': '3389', | ||
'adm': 'invalid response', | ||
'adomain': ['google.com'], | ||
'iurl': 'https://rtb.relevate.health/prebid/relevate', | ||
'cid': '1431/3389', | ||
'crid': '3389', | ||
'w': 160, | ||
'h': 600, | ||
'cat': ['IAB1-15'] | ||
}], | ||
'seat': '00001', | ||
'group': 0 | ||
}], | ||
'cur': 'USD', | ||
'bidid': 'BIDDER_1276' | ||
} | ||
}; | ||
}); | ||
|
||
describe('validations', function () { | ||
it('isBidValid : placementId and user_id are passed', function () { | ||
let bid = { | ||
bidder: 'relevatehealth', | ||
params: { | ||
placementId: 110011, | ||
user_id: '11211' | ||
} | ||
}, | ||
isValid = spec.isBidRequestValid(bid); | ||
expect(isValid).to.equals(true); | ||
}); | ||
it('isBidValid : placementId and user_id are not passed', function () { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. You should check if it fails validation when either user_id or placementId are missing. There should be multiple tests for multiple conditions. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Added the validations as asked. |
||
let bid = { | ||
bidder: 'relevatehealth', | ||
params: { | ||
width: 160, | ||
height: 600, | ||
domain: '', | ||
bid_floor: 0.5, | ||
user_id: '' | ||
} | ||
}, | ||
isValid = spec.isBidRequestValid(bid); | ||
expect(isValid).to.equals(false); | ||
}); | ||
}); | ||
describe('Validate Request', function () { | ||
it('Immutable bid request validate', function () { | ||
let _Request = utils.deepClone(request), | ||
bidRequest = spec.buildRequests(request); | ||
expect(request).to.deep.equal(_Request); | ||
}); | ||
it('Validate bidder connection', function () { | ||
let _Request = spec.buildRequests(request); | ||
expect(_Request.url).to.equal('https://rtb.relevate.health/prebid/relevate'); | ||
expect(_Request.method).to.equal('POST'); | ||
expect(_Request.options.contentType).to.equal('application/json'); | ||
}); | ||
it('Validate bid request : Impression', function () { | ||
let _Request = spec.buildRequests(request); | ||
let data = JSON.parse(_Request.data); | ||
// expect(data.at).to.equal(1); // auction type | ||
expect(data[0].imp[0].id).to.equal(request[0].bidId); | ||
expect(data[0].placementId).to.equal(110011); | ||
}); | ||
it('Validate bid request : ad size', function () { | ||
let _Request = spec.buildRequests(request); | ||
let data = JSON.parse(_Request.data); | ||
expect(data[0].imp[0].banner).to.be.a('object'); | ||
expect(data[0].imp[0].banner.w).to.equal(160); | ||
expect(data[0].imp[0].banner.h).to.equal(600); | ||
}); | ||
it('Validate bid request : user object', function () { | ||
let _Request = spec.buildRequests(request); | ||
let data = JSON.parse(_Request.data); | ||
expect(data[0].user).to.be.a('object'); | ||
expect(data[0].user.id).to.be.a('string'); | ||
}); | ||
it('Validate bid request : CCPA Check', function () { | ||
let bidRequest = { | ||
uspConsent: '1NYN' | ||
}; | ||
let _Request = spec.buildRequests(request, bidRequest); | ||
let data = JSON.parse(_Request.data); | ||
expect(data[0].us_privacy).to.equal('1NYN'); | ||
// let _bidRequest = {}; | ||
// let _Request1 = spec.buildRequests(request, _bidRequest); | ||
// let data1 = JSON.parse(_Request1.data); | ||
// expect(data1.regs).to.equal(undefined); | ||
}); | ||
}); | ||
describe('Validate response ', function () { | ||
it('Validate bid response : valid bid response', function () { | ||
let bRequest = spec.buildRequests(request); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Correct me if I'm wrong, but it doesn't seem like setting bRequest and data is actually doing anything in this test. Remove these two lines. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yes, you are right. Removed the lines. |
||
let data = JSON.parse(bRequest.data); | ||
let bResponse = spec.interpretResponse(bannerResponse, request); | ||
expect(bResponse).to.be.an('array').with.length.above(0); | ||
expect(bResponse[0].requestId).to.equal(bannerResponse.body.seatbid[0].bid[0].impid); | ||
expect(bResponse[0].width).to.equal(bannerResponse.body.seatbid[0].bid[0].w); | ||
expect(bResponse[0].height).to.equal(bannerResponse.body.seatbid[0].bid[0].h); | ||
expect(bResponse[0].currency).to.equal('USD'); | ||
expect(bResponse[0].netRevenue).to.equal(false); | ||
expect(bResponse[0].mediaType).to.equal('banner'); | ||
expect(bResponse[0].meta.advertiserDomains).to.deep.equal(['google.com']); | ||
expect(bResponse[0].ttl).to.equal(300); | ||
expect(bResponse[0].creativeId).to.equal(bannerResponse.body.seatbid[0].bid[0].crid); | ||
expect(bResponse[0].dealId).to.equal(bannerResponse.body.seatbid[0].bid[0].dealId); | ||
}); | ||
it('Invalid bid response check ', function () { | ||
let bRequest = spec.buildRequests(request); | ||
let response = spec.interpretResponse(invalidResponse, bRequest); | ||
expect(response[0].ad).to.equal('invalid response'); | ||
}); | ||
}); | ||
describe('GPP and coppa', function () { | ||
it('Request params check with GPP Consent', function () { | ||
let bidderReq = { gppConsent: { gppString: 'gpp-string-test', applicableSections: [5] } }; | ||
let _Request = spec.buildRequests(request, bidderReq); | ||
let data = JSON.parse(_Request.data); | ||
expect(data[0].gpp).to.equal('gpp-string-test'); | ||
expect(data[0].gpp_sid[0]).to.equal(5); | ||
}); | ||
it('Request params check with GPP Consent read from ortb2', function () { | ||
let bidderReq = { | ||
ortb2: { | ||
regs: { | ||
gpp: 'gpp-test-string', | ||
gpp_sid: [5] | ||
} | ||
} | ||
}; | ||
let _Request = spec.buildRequests(request, bidderReq); | ||
let data = JSON.parse(_Request.data); | ||
expect(data[0].gpp).to.equal('gpp-test-string'); | ||
expect(data[0].gpp_sid[0]).to.equal(5); | ||
}); | ||
it(' Bid request should have coppa flag if its true', () => { | ||
let bidderReq = { ortb2: { regs: { coppa: 1 } } }; | ||
let _Request = spec.buildRequests(request, bidderReq); | ||
let data = JSON.parse(_Request.data); | ||
expect(data[0].coppa).to.equal(1); | ||
}); | ||
}); | ||
}); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please also look to the getFloor function in the request
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Created a function getFloor() to get bid_floor.