Skip to content
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

NativoBidAdapter - Bid data mapping refactor and added QS params on request #8196

Merged
merged 34 commits into from
Mar 30, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
34 commits
Select commit Hold shift + click to select a range
38e1ade
Initial nativoBidAdapter document creation (js, md and spec)
jsfledd Feb 22, 2021
0792373
Fulling working prebid using nativoBidAdapter. Support for GDPR and C…
jsfledd Mar 12, 2021
008e906
Added defult size settings based on the largest ad unit. Added respon…
jsfledd Apr 5, 2021
21f3e0c
Changed bidder endpoint url
jsfledd Apr 7, 2021
1a33f2c
Changed double quotes to single quotes.
jsfledd Apr 7, 2021
517cd5b
Reverted package-json.lock to remove modifications from PR
jsfledd Apr 8, 2021
44bf428
Added optional bidder param 'url' so the ad server can force- match a…
jsfledd Apr 15, 2021
bfe0e1e
Lint fix. Added space after if.
jsfledd Apr 15, 2021
e927584
Added new QS param to send various adUnit data to adapter endpopint
jsfledd May 26, 2021
8ac4419
Merged latest prebid master branch
jsfledd May 26, 2021
26d8b86
Updated unit test for new QS param
jsfledd May 26, 2021
ead77de
Added qs param to keep track of ad unit refreshes
jsfledd Jul 20, 2021
4ed371c
Merged latest prebid master
jsfledd Jul 20, 2021
e59a770
Merged latest. Added adUnitCode as alternate to placementID. Removed …
jsfledd Sep 29, 2021
c338c4d
Updated bidMap key default value
jsfledd Sep 30, 2021
bfe7b02
Updated refresh increment logic
jsfledd Oct 12, 2021
06407a1
Merged latest
jsfledd Oct 25, 2021
978ffd7
Refactored spread operator for IE11 support
jsfledd Oct 25, 2021
f74f370
Updated isBidRequestValid check
jsfledd Oct 26, 2021
e1554d0
Merge branch 'master' of https://github.com/prebid/Prebid.js
jsfledd Oct 26, 2021
7b9c9bb
Refactored Object.enties to use Object.keys to fix CircleCI testing e…
jsfledd Oct 26, 2021
ffe4dc9
Merge remote-tracking branch 'Prebid_Official/master'
jsfledd Jan 14, 2022
7274900
Updated bid mapping key creation to prioritize ad unit code over plac…
jsfledd Jan 14, 2022
95cc80e
Added filtering by ad, advertiser and campaign.
jsfledd Feb 7, 2022
6b248fc
Merge remote-tracking branch 'Prebid_Official/master'
jsfledd Feb 7, 2022
bea02d0
Merge remote-tracking branch 'Prebid_Official/master'
jsfledd Feb 17, 2022
a6fc789
Merged master
jsfledd Feb 17, 2022
657b35e
merged with master
jsfledd Mar 14, 2022
2c0c8f5
Added more robust bidDataMap with multiple key access
jsfledd Mar 14, 2022
97f020d
Deduped filer values
jsfledd Mar 16, 2022
b23518f
Rolled back package.json
jsfledd Mar 30, 2022
2eeaa47
Merged with latest upstream master branch
jsfledd Mar 30, 2022
729e608
Duped upstream/master's package.lock file ... not sure how it got cha…
jsfledd Mar 30, 2022
f26e064
Small refactor of filterData length check. Removed comparison with 0 …
jsfledd Mar 30, 2022
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
122 changes: 112 additions & 10 deletions modules/nativoBidAdapter.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,71 @@ const TIME_TO_LIVE = 360

const SUPPORTED_AD_TYPES = [BANNER]

/**
* Keep track of bid data by keys
* @returns {Object} - Map of bid data that can be referenced by multiple keys
*/
const BidDataMap = () => {
const referenceMap = {}
const bids = []

/**
* Add a refence to the index by key value
* @param {String} key - The key to store the index reference
* @param {Integer} index - The index value of the bidData
*/
function adKeyReference(key, index) {
if (!referenceMap.hasOwnProperty(key)) {
referenceMap[key] = index
}
}

/**
* Adds a bid to the map
* @param {Object} bid - Bid data
* @param {Array/String} keys - Keys to reference the index value
*/
function addBidData(bid, keys) {
const index = bids.length
bids.push(bid)

if (Array.isArray(keys)) {
keys.forEach((key) => {
adKeyReference(String(key), index)
})
return
}

adKeyReference(String(keys), index)
}

/**
* Get's the bid data refrerenced by the key
* @param {String} key - The key value to find the bid data by
* @returns {Object} - The bid data
*/
function getBidData(key) {
const stringKey = String(key)
if (referenceMap.hasOwnProperty(stringKey)) {
return bids[referenceMap[stringKey]]
}
}

// Return API
return {
addBidData,
getBidData,
}
}

const bidRequestMap = {}
const adUnitsRequested = {}
const extData = {}

// Filtering
const adsToFilter = new Set()
const advertisersToFilter = new Set()
const campaignsToFilter = new Set()

// Prebid adapter referrence doc: https://docs.prebid.org/dev-docs/bidder-adaptor.html

Expand Down Expand Up @@ -45,7 +108,7 @@ export const spec = {
if (!bid.params) return bid.bidder === BIDDER_CODE

// Check if any supplied parameters are invalid
const hasInvalidParameters = Object.keys(bid.params).some(key => {
const hasInvalidParameters = Object.keys(bid.params).some((key) => {
const value = bid.params[key]
const validityCheck = validParameter[key]

Expand All @@ -69,8 +132,8 @@ export const spec = {
*/
buildRequests: function (validBidRequests, bidderRequest) {
const placementIds = new Set()
const placmentBidIdMap = {}
let placementId, pageUrl
const bidDataMap = BidDataMap()
validBidRequests.forEach((request) => {
pageUrl = deepAccess(
request,
Expand All @@ -83,13 +146,13 @@ export const spec = {
placementIds.add(placementId)
}

var key = placementId || request.adUnitCode
placmentBidIdMap[key] = {
const bidData = {
bidId: request.bidId,
size: getLargestSize(request.sizes),
}
bidDataMap.addBidData(bidData, [placementId, request.adUnitCode])
})
bidRequestMap[bidderRequest.bidderRequestId] = placmentBidIdMap
bidRequestMap[bidderRequest.bidderRequestId] = bidDataMap

// Build adUnit data
const adUnitData = {
Expand Down Expand Up @@ -123,6 +186,20 @@ export const spec = {
},
]

// Add filtering
if (adsToFilter.size > 0) {
params.unshift({ key: 'ntv_atf', value: Array.from(adsToFilter).join(',') })
}

if (advertisersToFilter.size > 0) {
params.unshift({ key: 'ntv_avtf', value: Array.from(advertisersToFilter).join(',') })
}

if (campaignsToFilter.size > 0) {
params.unshift({ key: 'ntv_ctf', value: Array.from(campaignsToFilter).join(',') })
}

// Add placement IDs
if (placementIds.size > 0) {
// Convert Set to Array (IE 11 Safe)
const placements = []
Expand All @@ -131,6 +208,7 @@ export const spec = {
params.unshift({ key: 'ntv_ptd', value: placements.join(',') })
}

// Add GDPR params
if (bidderRequest.gdprConsent) {
// Put on the beginning of the qs param array
params.unshift({
Expand All @@ -139,6 +217,7 @@ export const spec = {
})
}

// Add USP params
if (bidderRequest.uspConsent) {
// Put on the beginning of the qs param array
params.unshift({ key: 'us_privacy', value: bidderRequest.uspConsent })
Expand Down Expand Up @@ -195,6 +274,8 @@ export const spec = {
},
}

if (bid.ext) extData[bid.id] = bid.ext

bidResponses.push(bidResponse)
})
})
Expand Down Expand Up @@ -300,7 +381,15 @@ export const spec = {
* Will be called when a bid from the adapter won the auction.
* @param {Object} bid - The bid that won the auction
*/
onBidWon: function (bid) {},
onBidWon: function (bid) {
const ext = extData[bid.dealId]

if (!ext) return

appendFilterData(adsToFilter, ext.adsToFilter)
appendFilterData(advertisersToFilter, ext.advertisersToFilter)
appendFilterData(campaignsToFilter, ext.campaignsToFilter)
},

/**
* Will be called when the adserver targeting has been set for a bid from the adapter.
Expand All @@ -315,12 +404,14 @@ export const spec = {
* @returns {String} - The bidId value associated with the corresponding placementId
*/
getAdUnitData: function (bidderRequestId, bid) {
var data = deepAccess(bidRequestMap, `${bidderRequestId}.${bid.impid}`)
const bidDataMap = bidRequestMap[bidderRequestId]

if (data) return data
const placementId = bid.impid
const adUnitCode = deepAccess(bid, 'ext.ad_unit_id')

var unitCode = deepAccess(bid, 'ext.ad_unit_id')
return deepAccess(bidRequestMap, `${bidderRequestId}.${unitCode}`)
return (
bidDataMap.getBidData(adUnitCode) || bidDataMap.getBidData(placementId)
)
},
}
registerBidder(spec)
Expand Down Expand Up @@ -375,3 +466,14 @@ function getLargestSize(sizes, method = area) {
* @returns The calculated area
*/
const area = (size) => size[0] * size[1]

/**
* Save any filter data from winning bid requests for subsequent requests
* @param {Array} filter - The filter data bucket currently stored
* @param {Array} filterData - The filter data to add
*/
function appendFilterData(filter, filterData) {
if (filterData && Array.isArray(filterData) && filterData.length) {
filterData.forEach((ad) => filter.add(ad))
}
}
167 changes: 155 additions & 12 deletions test/spec/modules/nativoBidAdapter_spec.js
Original file line number Diff line number Diff line change
@@ -1,14 +1,10 @@
import { expect } from 'chai'
import { spec } from 'modules/nativoBidAdapter.js'
// import { newBidder } from 'src/adapters/bidderFactory.js'
// import * as bidderFactory from 'src/adapters/bidderFactory.js'
// import { deepClone } from 'src/utils.js'
// import { config } from 'src/config.js'

describe('nativoBidAdapterTests', function () {
describe('isBidRequestValid', function () {
let bid = {
bidder: 'nativo'
bidder: 'nativo',
}

it('should return true if no params found', function () {
Expand Down Expand Up @@ -273,9 +269,7 @@ describe('getAdUnitData', () => {
}

const data = spec.getAdUnitData(9876543, { impid: 12345 })
expect(Object.keys(data)).to.have.deep.members(
Object.keys(adUnitData)
)
expect(Object.keys(data)).to.have.deep.members(Object.keys(adUnitData))
})

it('Falls back to ad unit code value', () => {
Expand All @@ -290,9 +284,158 @@ describe('getAdUnitData', () => {
},
}

const data = spec.getAdUnitData(9876543, { impid: 12345, ext: { ad_unit_code: '#test-code' } })
expect(Object.keys(data)).to.have.deep.members(
Object.keys(adUnitData)
)
const data = spec.getAdUnitData(9876543, {
impid: 12345,
ext: { ad_unit_code: '#test-code' },
})
expect(Object.keys(data)).to.have.deep.members(Object.keys(adUnitData))
})
})

describe('Response to Request Filter Flow', () => {
let bidRequests = [
{
bidder: 'nativo',
params: {
placementId: '10433394',
},
adUnitCode: 'adunit-code',
sizes: [
[300, 250],
[300, 600],
],
bidId: '27b02036ccfa6e',
bidderRequestId: '1372cd8bd8d6a8',
auctionId: 'cfc467e4-2707-48da-becb-bcaab0b2c114',
transactionId: '3b36e7e0-0c3e-4006-a279-a741239154ff',
},
]

let response

beforeEach(() => {
response = {
id: '126456',
seatbid: [
{
seat: 'seat_0',
bid: [
{
id: 'f70362ac-f3cf-4225-82a5-948b690927a6',
impid: '1',
price: 3.569,
adm: '<creative>',
h: 300,
w: 250,
cat: [],
adomain: ['test.com'],
crid: '1060_72_6760217',
},
],
},
],
cur: 'USD',
}
})

let bidderRequest = {
id: 123456,
bids: [
{
params: {
placementId: 1,
},
},
],
}

// mock
spec.getAdUnitData = () => {
return {
bidId: 123456,
size: [300, 250],
}
}

it('Appends NO filter based on previous response', () => {
// Getting the mock response
let result = spec.interpretResponse({ body: response }, { bidderRequest })

// Winning the bid
spec.onBidWon(result[0])

// Making another request
const request = spec.buildRequests(bidRequests, {
bidderRequestId: 123456,
refererInfo: {
referer: 'https://www.test.com',
},
})
expect(request.url).to.not.include('ntv_aft')
expect(request.url).to.not.include('ntv_avtf')
expect(request.url).to.not.include('ntv_ctf')
})

it('Appends Ads filter based on previous response', () => {
response.seatbid[0].bid[0].ext = { adsToFilter: ['12345'] }

// Getting the mock response
let result = spec.interpretResponse({ body: response }, { bidderRequest })

// Winning the bid
spec.onBidWon(result[0])

// Making another request
const request = spec.buildRequests(bidRequests, {
bidderRequestId: 123456,
refererInfo: {
referer: 'https://www.test.com',
},
})
expect(request.url).to.include(`ntv_atf=12345`)
expect(request.url).to.not.include('ntv_avtf')
expect(request.url).to.not.include('ntv_ctf')
})

it('Appends Advertiser filter based on previous response', () => {
response.seatbid[0].bid[0].ext = { advertisersToFilter: ['1'] }

// Getting the mock response
let result = spec.interpretResponse({ body: response }, { bidderRequest })

// Winning the bid
spec.onBidWon(result[0])

// Making another request
const request = spec.buildRequests(bidRequests, {
bidderRequestId: 123456,
refererInfo: {
referer: 'https://www.test.com',
},
})
expect(request.url).to.include(`ntv_atf=12345`)
expect(request.url).to.include('ntv_avtf=1')
expect(request.url).to.not.include('ntv_ctf')
})

it('Appends Campaign filter based on previous response', () => {
response.seatbid[0].bid[0].ext = { campaignsToFilter: ['234'] }

// Getting the mock response
let result = spec.interpretResponse({ body: response }, { bidderRequest })

// Winning the bid
spec.onBidWon(result[0])

// Making another request
const request = spec.buildRequests(bidRequests, {
bidderRequestId: 123456,
refererInfo: {
referer: 'https://www.test.com',
},
})
expect(request.url).to.include(`ntv_atf=12345`)
expect(request.url).to.include('ntv_avtf=1')
expect(request.url).to.include('ntv_ctf=234')
})
})