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

Nativo Bid Adapter: Adding UserId support #9767

Merged
merged 57 commits into from
Apr 17, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
57 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
024d010
Merge branch 'upstream_master'
jsfledd Apr 20, 2022
060ff4c
Added bid sizes to request
jsfledd Jun 6, 2022
15d34f1
Fixed function name in spec. Added unit tests.
jsfledd Jun 6, 2022
4ff5d1b
Merge branch 'upstream_master'
jsfledd Jul 8, 2022
7874280
Added priceFloor module support
jsfledd Jul 8, 2022
7619590
Added protection agains empty url parameter
jsfledd Jul 13, 2022
0bdc853
Merge branch 'upstream_master'
jsfledd Jul 13, 2022
ee52fbb
Merged with latest
jsfledd Oct 5, 2022
c5b8fab
Changed ntv_url QS param to use referrer.location instead of referrer…
jsfledd Oct 5, 2022
51ba485
Removed testing 'only' flag
jsfledd Oct 5, 2022
b682e0b
Merge branch 'upstream_master'
jsfledd Dec 9, 2022
1c02303
Merge branch 'upstream_master'
jsfledd Dec 13, 2022
b6ba5b5
Added ntv_url QS param value validation
jsfledd Dec 13, 2022
e2bed09
Merge remote-tracking branch 'PrebidOfficial/master'
jsfledd Feb 10, 2023
b629344
Merge remote-tracking branch 'PrebidOfficial/master'
jsfledd Feb 23, 2023
e04cf3d
Added userId support
jsfledd Feb 23, 2023
e9bb4ab
Added unit tests, refactored for bugs
jsfledd Feb 23, 2023
ce1b931
Wrapped ajax in try/catch
jsfledd Feb 23, 2023
a3af4e2
Added more unit testing
jsfledd Feb 23, 2023
883ebd3
Updated eid check for duplicate values. Removed error logging as we n…
jsfledd Mar 3, 2023
be48d28
Removed spec test .only. Fixed unit tests that were breaking.
jsfledd Mar 20, 2023
79880a8
Added Prebid version to nativo exchange request
jsfledd Apr 14, 2023
dbc8618
Removed unused bidder methods
jsfledd Apr 14, 2023
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
131 changes: 110 additions & 21 deletions modules/nativoBidAdapter.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { deepAccess, isEmpty } from '../src/utils.js'
import { registerBidder } from '../src/adapters/bidderFactory.js'
import { BANNER } from '../src/mediaTypes.js'
import { getGlobal } from '../src/prebidGlobal.js'
// import { config } from 'src/config'

const BIDDER_CODE = 'nativo'
Expand All @@ -14,6 +15,8 @@ const SUPPORTED_AD_TYPES = [BANNER]
const FLOOR_PRICE_CURRENCY = 'USD'
const PRICE_FLOOR_WILDCARD = '*'

const localPbjsRef = getGlobal()

/**
* Keep track of bid data by keys
* @returns {Object} - Map of bid data that can be referenced by multiple keys
Expand Down Expand Up @@ -133,6 +136,9 @@ export const spec = {
* @return ServerRequest Info describing the request to the server.
*/
buildRequests: function (validBidRequests, bidderRequest) {
const requestData = new RequestData()
requestData.addBidRequestDataSource(new UserEIDs())

// Parse values from bid requests
const placementIds = new Set()
const bidDataMap = BidDataMap()
Expand Down Expand Up @@ -166,6 +172,8 @@ export const spec = {
if (bidRequestFloorPriceData) {
floorPriceData[bidRequest.adUnitCode] = bidRequestFloorPriceData
}

requestData.processBidRequestData(bidRequest, bidderRequest)
})
bidRequestMap[bidderRequest.bidderRequestId] = bidDataMap

Expand All @@ -174,6 +182,10 @@ export const spec = {

// Build basic required QS Params
let params = [
// Prebid version
{
key: 'ntv_pbv', value: localPbjsRef.version
},
// Prebid request id
{ key: 'ntv_pb_rid', value: bidderRequest.bidderRequestId },
// Ad unit data
Expand Down Expand Up @@ -255,9 +267,12 @@ export const spec = {
params.unshift({ key: 'us_privacy', value: bidderRequest.uspConsent })
}

const qsParamStrings = [requestData.getRequestDataQueryString(), arrayToQS(params)]
const requestUrl = buildRequestUrl(BIDDER_ENDPOINT, qsParamStrings)

let serverRequest = {
method: 'GET',
url: BIDDER_ENDPOINT + arrayToQS(params),
url: requestUrl
}

return serverRequest
Expand Down Expand Up @@ -404,13 +419,6 @@ export const spec = {
return syncs
},

/**
* Will be called when an adpater timed out for an auction.
* Adapter can fire a ajax or pixel call to register a timeout at thier end.
* @param {Object} timeoutData - Timeout specific data
*/
onTimeout: function (timeoutData) {},

/**
* Will be called when a bid from the adapter won the auction.
* @param {Object} bid - The bid that won the auction
Expand All @@ -425,12 +433,6 @@ export const spec = {
appendFilterData(campaignsToFilter, ext.campaignsToFilter)
},

/**
* Will be called when the adserver targeting has been set for a bid from the adapter.
* @param {Object} bidder - The bid of which the targeting has been set
*/
onSetTargeting: function (bid) {},

/**
* Maps Prebid's bidId to Nativo's placementId values per unique bidderRequestId
* @param {String} bidderRequestId - The unique ID value associated with the bidderRequest
Expand All @@ -451,6 +453,78 @@ export const spec = {
registerBidder(spec)

// Utils
export class RequestData {
constructor() {
this.bidRequestDataSources = []
}

addBidRequestDataSource(bidRequestDataSource) {
if (!(bidRequestDataSource instanceof BidRequestDataSource)) return

this.bidRequestDataSources.push(bidRequestDataSource)
}

processBidRequestData(bidRequest, bidderRequest) {
for (let bidRequestDataSource of this.bidRequestDataSources) {
bidRequestDataSource.processBidRequestData(bidRequest, bidderRequest)
}
}

getRequestDataQueryString() {
if (this.bidRequestDataSources.length == 0) return

const queryParams = this.bidRequestDataSources.map(dataSource => dataSource.getRequestQueryString()).filter(queryString => queryString !== '')
return queryParams.join('&')
}
}

export class BidRequestDataSource {
constructor() {
this.type = 'BidRequestDataSource'
}
processBidRequestData(bidRequest, bidderRequest) { }
getRequestQueryString() { return '' }
}

export class UserEIDs extends BidRequestDataSource {
constructor() {
super()
this.type = 'UserEIDs'
this.qsParam = new QueryStringParam('ntv_pb_eid')
this.eids = []
}

processBidRequestData(bidRequest, bidderRequest) {
if (bidRequest.userIdAsEids === undefined || this.eids.length > 0) return
this.eids = bidRequest.userIdAsEids
}

getRequestQueryString() {
if (this.eids.length === 0) return ''

const encodedValueArray = encodeToBase64(this.eids)
this.qsParam.value = encodedValueArray
return this.qsParam.toString()
}
}

export class QueryStringParam {
constructor(key, value) {
this.key = key
this.value = value
}
}

QueryStringParam.prototype.toString = function () {
return `${this.key}=${this.value}`
}

export function encodeToBase64(value) {
try {
return btoa(JSON.stringify(value))
} catch (err) { }
}

export function parseFloorPriceData(bidRequest) {
if (typeof bidRequest.getFloor !== 'function') return

Expand Down Expand Up @@ -589,12 +663,9 @@ function appendQSParamString(str, key, value) {
* @returns
*/
function arrayToQS(arr) {
return (
'?' +
arr.reduce((value, obj) => {
return appendQSParamString(value, obj.key, obj.value)
}, '')
)
return arr.reduce((value, obj) => {
return appendQSParamString(value, obj.key, obj.value)
}, '')
}

/**
Expand All @@ -615,6 +686,24 @@ function getLargestSize(sizes, method = area) {
})
}

/**
* Build the final request url
*/
export function buildRequestUrl(baseUrl, qsParamStringArray = []) {
if (qsParamStringArray.length === 0 || !Array.isArray(qsParamStringArray)) return baseUrl

const nonEmptyQSParamStrings = qsParamStringArray.filter(qsParamString => qsParamString.trim() !== '')

if (nonEmptyQSParamStrings.length === 0) return baseUrl

let requestUrl = `${baseUrl}?${nonEmptyQSParamStrings[0]}`
for (let i = 1; i < nonEmptyQSParamStrings.length; i++) {
requestUrl += `&${nonEmptyQSParamStrings[i]}`
}

return requestUrl
}

/**
* Calculate the area
* @param {Array} size - [width, height]
Expand Down Expand Up @@ -645,7 +734,7 @@ export function getPageUrlFromBidRequest(bidRequest) {
try {
const url = new URL(paramPageUrl)
return url.href
} catch (err) {}
} catch (err) { }
}

export function hasProtocol(url) {
Expand Down
101 changes: 101 additions & 0 deletions test/spec/modules/nativoBidAdapter_spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,10 @@ import {
getPageUrlFromBidRequest,
hasProtocol,
addProtocol,
BidRequestDataSource,
RequestData,
UserEIDs,
buildRequestUrl,
} from '../../../modules/nativoBidAdapter'

describe('bidDataMap', function () {
Expand Down Expand Up @@ -120,6 +124,7 @@ describe('nativoBidAdapterTests', function () {
expect(request.url).to.be.a('string')

expect(request.url).to.include('?')
expect(request.url).to.include('ntv_pbv')
expect(request.url).to.include('ntv_ptd')
expect(request.url).to.include('ntv_pb_rid')
expect(request.url).to.include('ntv_ppc')
Expand Down Expand Up @@ -731,3 +736,99 @@ describe('getPageUrlFromBidRequest', () => {
expect(url).not.to.be.undefined
})
})

describe('RequestData', () => {
describe('addBidRequestDataSource', () => {
it('Adds a BidRequestDataSource', () => {
const requestData = new RequestData()
const testBidRequestDataSource = new BidRequestDataSource()

requestData.addBidRequestDataSource(testBidRequestDataSource)

expect(requestData.bidRequestDataSources.length == 1)
})

it("Doeasn't add a non BidRequestDataSource", () => {
const requestData = new RequestData()

requestData.addBidRequestDataSource({})
requestData.addBidRequestDataSource('test')
requestData.addBidRequestDataSource(1)
requestData.addBidRequestDataSource(true)

expect(requestData.bidRequestDataSources.length == 0)
})
})

describe('getRequestDataString', () => {
it("Doesn't append empty query strings", () => {
const requestData = new RequestData()
const testBidRequestDataSource = new BidRequestDataSource()

requestData.addBidRequestDataSource(testBidRequestDataSource)

let qs = requestData.getRequestDataQueryString()
expect(qs).to.be.empty

testBidRequestDataSource.getRequestQueryString = () => {
return 'ntv_test=true'
}
qs = requestData.getRequestDataQueryString()
expect(qs).to.be.equal('ntv_test=true')
})
})
})

describe('UserEIDs', () => {
const userEids = new UserEIDs()
const eids = [{ 'testId': 1111 }]

describe('processBidRequestData', () => {
it('Processes bid request without eids', () => {
userEids.processBidRequestData({})

expect(userEids.eids).to.be.empty
})

it('Processed bid request with eids', () => {
userEids.processBidRequestData({ userIdAsEids: eids })

expect(userEids.eids).to.not.be.empty
})
})

describe('getRequestQueryString', () => {
it('Correctly prints out QS param string', () => {
const qs = userEids.getRequestQueryString()
const value = qs.slice(11)

expect(qs).to.include('ntv_pb_eid=')
try {
expect(JSON.parse(value)).to.be.equal(eids)
} catch (err) { }
})
})
})

describe('buildRequestUrl', () => {
const baseUrl = 'https://www.testExchange.com'
it('Returns baseUrl if no QS strings passed', () => {
const url = buildRequestUrl(baseUrl)
expect(url).to.be.equal(baseUrl)
})

it('Returns baseUrl if empty QS strings passed', () => {
const url = buildRequestUrl(baseUrl, ['', '', ''])
expect(url).to.be.equal(baseUrl)
})

it('Returns baseUrl + QS params if QS strings passed', () => {
const url = buildRequestUrl(baseUrl, ['ntv_ptd=123456&ntv_test=true', 'ntv_foo=bar'])
expect(url).to.be.equal(`${baseUrl}?ntv_ptd=123456&ntv_test=true&ntv_foo=bar`)
})

it('Returns baseUrl + QS params if mixed QS strings passed', () => {
const url = buildRequestUrl(baseUrl, ['ntv_ptd=123456&ntv_test=true', '', '', 'ntv_foo=bar'])
expect(url).to.be.equal(`${baseUrl}?ntv_ptd=123456&ntv_test=true&ntv_foo=bar`)
})
})