Skip to content

Commit

Permalink
NoBid Bid Adapter : add support for first party user id (#10519)
Browse files Browse the repository at this point in the history
* Enable supplyChain support

* Added support for COPPA

* rebuilt

* Added support for Extended User IDs.

* Added support for the "meta" attribute in bid response.

* Delete nobidBidAdapter.js.orig

* Delete a

* Delete .jsdtscope

* Delete org.eclipse.wst.jsdt.ui.superType.container

* Delete org.eclipse.wst.jsdt.ui.superType.name

* Delete .project

* Fix tests

* Added support for First Party User ID in NoBid Bid Adapter.

---------

Co-authored-by: Reda Guermas <reda.guermas@nobid.io>
  • Loading branch information
redaguermas and Reda Guermas authored Oct 3, 2023
1 parent ef0a7c8 commit 7600b38
Show file tree
Hide file tree
Showing 2 changed files with 212 additions and 40 deletions.
123 changes: 122 additions & 1 deletion modules/nobidBidAdapter.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,17 @@ import { hasPurpose1Consent } from '../src/utils/gpdr.js';
const GVLID = 816;
const BIDDER_CODE = 'nobid';
const storage = getStorageManager({bidderCode: BIDDER_CODE});
window.nobidVersion = '1.3.3';
window.nobidVersion = '1.4.1';
window.nobid = window.nobid || {};
window.nobid.bidResponses = window.nobid.bidResponses || {};
window.nobid.timeoutTotal = 0;
window.nobid.bidWonTotal = 0;
window.nobid.refreshCount = 0;
window.nobid.firstPartyIds = null;
window.nobid.firstPartyIdEnabled = false;
const FIRST_PARTY_KEY = 'fppcid.nobid.io';
const FIRST_PARTY_SOURCE_KEY = 'fpid.nobid.io';
const FIRST_PARTY_DATA_EXPIRY_DAYS = 7 * 24 * 3600 * 1000;
function log(msg, obj) {
logInfo('-NoBid- ' + msg, obj)
}
Expand Down Expand Up @@ -135,8 +140,10 @@ function nobidBuildRequests(bids, bidderRequest) {
src.push({source: eid.source, uids: ids});
}
});
if (window.nobid.firstPartyIds && window.nobid.firstPartyIds) src.push({source: FIRST_PARTY_SOURCE_KEY, uids: [{id: window.nobid.firstPartyIds.ids}]});
return src;
}
if (window.nobid.firstPartyIds && window.nobid.firstPartyIds.ids) return [{source: FIRST_PARTY_SOURCE_KEY, uids: [{id: window.nobid.firstPartyIds.ids}]}];
}
var state = {};
state['sid'] = siteId;
Expand Down Expand Up @@ -286,13 +293,20 @@ function nobidInterpretResponse(response, bidRequest) {
var setRefreshLimit = function(response) {
if (response && typeof response.rlimit !== 'undefined') window.nobid.refreshLimit = response.rlimit;
}
var setFirstPartyIdEnabled = function(response) {
if (response && typeof response.fpid !== 'undefined') window.nobid.firstPartyIdEnabled = response.fpid;
if (window?.nobid?.firstPartyIdEnabled) {
nobidFirstPartyData.loadOrCreateFirstPartyData();
}
}
var setUserBlock = function(response) {
if (response && typeof response.ublock !== 'undefined') {
nobidSetCookie('_ublock', '1', response.ublock);
}
}
setRefreshLimit(response);
setUserBlock(response);
setFirstPartyIdEnabled(response);
var bidResponses = [];
for (var i = 0; response.bids && i < response.bids.length; i++) {
var bid = response.bids[i];
Expand Down Expand Up @@ -359,6 +373,113 @@ window.addEventListener('message', function (event) {
}
}
}, false);
const nobidFirstPartyData = {
isJson: function (str) {
return str && str.startsWith('{') && str.endsWith('}');
},
hasLocalStorage: function () {
try {
return window.localStorage;
} catch (error) {
logWarn('Local storage api disabled', error);
}
return false;
},
readFirstPartyDataIds: function () {
try {
if (this.hasLocalStorage()) {
const idsStr = window.localStorage.getItem(FIRST_PARTY_SOURCE_KEY);
if (this.isJson(idsStr)) {
const idsObj = JSON.parse(idsStr);
if (idsObj.ts + FIRST_PARTY_DATA_EXPIRY_DAYS < Date.now()) return { pid: idsObj.pid }; // expired?
return idsObj;
}
return null;
}
} catch (error) {
logWarn('Local storage api disabled', error);
}
return null;
},
loadOrCreateFirstPartyData: function () {
const storeFirstPartyDataIds = function ({ids: theIds, pid: thePid}) {
try {
if (nobidFirstPartyData.hasLocalStorage()) {
window.localStorage.setItem(FIRST_PARTY_SOURCE_KEY, JSON.stringify({ids: theIds, pid: thePid, ts: Date.now()}));
}
} catch (error) {
logWarn('Local storage api disabled', error);
}
};
const readFirstPartyId = function () {
try {
if (nobidFirstPartyData.hasLocalStorage()) {
const idStr = window.localStorage.getItem(FIRST_PARTY_KEY);
if (nobidFirstPartyData.isJson(idStr)) {
return JSON.parse(idStr);
}
return null;
}
} catch (error) {
logWarn('Local storage api disabled', error);
}
return null;
};
const storeFirstPartyId = function (theId) {
try {
if (nobidFirstPartyData.hasLocalStorage()) {
window.localStorage.setItem(FIRST_PARTY_KEY, JSON.stringify(theId));
}
} catch (error) {
logWarn('Local storage api disabled', error);
}
};
const _loadOrCreateFirstPartyData = function () {
const generateGUID = function () {
let d = new Date().getTime();
return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function (c) {
const r = (d + Math.random() * 16) % 16 | 0;
d = Math.floor(d / 16);
return (c === 'x' ? r : (r & 0x3) | 0x8).toString(16);
});
};
const ajaxGet = function (ajaxParams, callback) {
const ajax = new XMLHttpRequest();
ajax.withCredentials = false;
ajax.timeout = ajaxParams.timeout;
ajax.open('GET', ajaxParams.url, true);
ajax.onreadystatechange = function () {
if (this.readyState === XMLHttpRequest.DONE) {
callback(this.response);
}
};
ajax.send(ajaxParams.data);
};
let firstPartyIdObj = readFirstPartyId();
if (!firstPartyIdObj || !firstPartyIdObj.id || !firstPartyIdObj.ts) {
const firstPartyId = generateGUID();
firstPartyIdObj = {id: firstPartyId, ts: Date.now()};
storeFirstPartyId(firstPartyIdObj);
}
let firstPartyIds = nobidFirstPartyData.readFirstPartyDataIds();
if (firstPartyIdObj?.ts && !firstPartyIds?.ids) {
const pid = firstPartyIds?.pid || '';
const pdate = firstPartyIdObj.ts;
const firstPartyId = firstPartyIdObj.id;
const url = `https://api.intentiq.com/profiles_engine/ProfilesEngineServlet?at=39&mi=10&pt=17&dpn=1&iiqidtype=2&dpi=430542822&iiqpcid=${firstPartyId}&iiqpciddate=${pdate}&pid=${pid}`;
if (window.nobid.firstPartyRequestInProgress) return;
window.nobid.firstPartyRequestInProgress = true;
ajaxGet({ url: url }, function (response) {
response = JSON.parse(response);
if (response?.data) storeFirstPartyDataIds({ ids: response.data, pid: response.pid });
});
}
};
window.nobid.firstPartyIds = this.readFirstPartyDataIds();
if (window.nobid.firstPartyIdEnabled && !window.nobid.firstPartyIds?.ids) _loadOrCreateFirstPartyData();
}
};
window.nobid.firstPartyIds = nobidFirstPartyData.readFirstPartyDataIds();
export const spec = {
code: BIDDER_CODE,
gvlid: GVLID,
Expand Down
129 changes: 90 additions & 39 deletions test/spec/modules/nobidBidAdapter_spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -57,27 +57,26 @@ describe('Nobid Adapter', function () {
'auctionId': '1d1a030790a475',
};

it('should return true when required params found', function () {
it('should return true when required params found 1', function () {
expect(spec.isBidRequestValid(bid)).to.equal(true);
});

it('should return true when required params found', function () {
let bid = Object.assign({}, bid);
delete bid.params;
bid.params = {
it('should return true when required params found 2', function () {
let mybid = Object.assign({}, bid);
delete mybid.params;
mybid.params = {
'siteId': 2
};

expect(spec.isBidRequestValid(bid)).to.equal(true);
expect(spec.isBidRequestValid(mybid)).to.equal(true);
});

it('should return false when required params are not passed', function () {
let bid = Object.assign({}, bid);
delete bid.params;
bid.params = {
let mybid = Object.assign({}, bid);
delete mybid.params;
mybid.params = {
'siteId': 0
};
expect(spec.isBidRequestValid(bid)).to.equal(false);
expect(spec.isBidRequestValid(mybid)).to.equal(false);
});
});

Expand Down Expand Up @@ -253,12 +252,12 @@ describe('Nobid Adapter', function () {
});

it('sends bid request to site id', function () {
const request = spec.buildRequests(bidRequests);
const payload = JSON.parse(request.data);
expect(payload.a).to.exist;
expect(payload.a[0].sid).to.equal(2);
expect(payload.a[0].at).to.equal('banner');
expect(payload.a[0].params.siteId).to.equal(2);
const request = spec.buildRequests(bidRequests);
const payload = JSON.parse(request.data);
expect(payload.a).to.exist;
expect(payload.a[0].sid).to.equal(2);
expect(payload.a[0].at).to.equal('banner');
expect(payload.a[0].params.siteId).to.equal(2);
});

it('sends bid request to ad type', function () {
Expand Down Expand Up @@ -423,6 +422,58 @@ describe('Nobid Adapter', function () {
});
});

describe('First Party ID Test', function () {
const CREATIVE_ID_300x250 = 'CREATIVE-100';
const ADUNIT_300x250 = 'ADUNIT-1';
const ADMARKUP_300x250 = 'ADMARKUP-300x250';
const PRICE_300x250 = 0.51;
const REQUEST_ID = '3db3773286ee59';
const DEAL_ID = 'deal123';
let response = {
country: 'US',
ip: '68.83.15.75',
device: 'COMPUTER',
site: 2,
fpid: true,
bids: [
{id: 1,
bdrid: 101,
divid: ADUNIT_300x250,
creativeid: CREATIVE_ID_300x250,
size: {'w': 300, 'h': 250},
adm: ADMARKUP_300x250,
price: '' + PRICE_300x250
}
]
};

it('first party ID', function () {
const bidderRequest = {
bids: [{
bidId: REQUEST_ID,
adUnitCode: ADUNIT_300x250
}]
}
const bidRequests = [
{
'bidder': 'nobid',
'params': {
'siteId': 2
},
'adUnitCode': 'adunit-code',
'sizes': [[300, 250]],
'bidId': '30b31c1838de1e',
'bidderRequestId': '22edbae2733bf6',
'auctionId': '1d1a030790a475',
}
];
const request = spec.buildRequests(bidRequests, bidderRequest);
expect(request.method).to.equal('POST');
spec.interpretResponse({ body: response }, {bidderRequest: bidderRequest});
expect(window.nobid.firstPartyIdEnabled).to.equal(true);
});
});

describe('isVideoBidRequestValid', function () {
let bid = {
bidder: 'nobid',
Expand Down Expand Up @@ -635,12 +686,12 @@ describe('Nobid Adapter', function () {
});

it('sends bid request to site id', function () {
const request = spec.buildRequests(bidRequests);
const payload = JSON.parse(request.data);
expect(payload.a).to.exist;
expect(payload.a[0].sid).to.equal(2);
expect(payload.a[0].at).to.equal('banner');
expect(payload.a[0].params.siteId).to.equal(2);
const request = spec.buildRequests(bidRequests);
const payload = JSON.parse(request.data);
expect(payload.a).to.exist;
expect(payload.a[0].sid).to.equal(2);
expect(payload.a[0].at).to.equal('banner');
expect(payload.a[0].params.siteId).to.equal(2);
});

it('sends bid request to ad type', function () {
Expand Down Expand Up @@ -939,19 +990,19 @@ describe('Nobid Adapter', function () {
auctionId: '1d1a030790a475',
coppa: true,
schain: {
validation: 'strict',
config: {
ver: '1.0',
complete: 1,
nodes: [
{
asi: 'indirectseller.com',
sid: '00001',
name: 'name.com',
hp: 1
}
]
}
validation: 'strict',
config: {
ver: '1.0',
complete: 1,
nodes: [
{
asi: 'indirectseller.com',
sid: '00001',
name: 'name.com',
hp: 1
}
]
}
}
}
];
Expand Down Expand Up @@ -1001,7 +1052,7 @@ describe('Nobid Adapter', function () {
]
};

it('should ULimit be respected', function () {
it('Limit should be respected', function () {
const bidderRequest = {
bids: [{
bidId: REQUEST_ID,
Expand Down Expand Up @@ -1060,8 +1111,8 @@ describe('Nobid Adapter', function () {
});

it('should get correct user sync when !iframeEnabled', function () {
let pixel = spec.getUserSyncs({})
expect(pixel.length).to.equal(0);
let pixel = spec.getUserSyncs({})
expect(pixel.length).to.equal(0);
});
});

Expand Down

0 comments on commit 7600b38

Please sign in to comment.