Skip to content

Commit 6f531d5

Browse files
authored
Add support for Publisher Common ID Module (#5871)
- New user id value to be sent to STR Ad Server as `pubcid` of the bid request object Story: [#175125639](https://www.pivotaltracker.com/story/show/175125639)
1 parent 894c1d7 commit 6f531d5

File tree

2 files changed

+79
-54
lines changed

2 files changed

+79
-54
lines changed

modules/sharethroughBidAdapter.js

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,12 @@ export const sharethroughAdapterSpec = {
5050
query.ttduid = bidRequest.userId.tdid;
5151
}
5252

53+
if (bidRequest.userId && bidRequest.userId.pubcid) {
54+
query.pubcid = bidRequest.userId.pubcid;
55+
} else if (bidRequest.crumbs && bidRequest.crumbs.pubcid) {
56+
query.pubcid = bidRequest.crumbs.pubcid;
57+
}
58+
5359
if (bidRequest.schain) {
5460
query.schain = JSON.stringify(bidRequest.schain);
5561
}

test/spec/modules/sharethroughBidAdapter_spec.js

Lines changed: 73 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,13 @@ const bidRequests = [
1212
params: {
1313
pkey: 'aaaa1111'
1414
},
15-
userId: { tdid: 'fake-tdid' }
15+
userId: {
16+
tdid: 'fake-tdid',
17+
pubcid: 'fake-pubcid'
18+
},
19+
crumbs: {
20+
pubcid: 'fake-pubcid-in-crumbs-obj'
21+
}
1622
},
1723
{
1824
bidder: 'sharethrough',
@@ -33,8 +39,8 @@ const bidRequests = [
3339
pkey: 'cccc3333',
3440
iframe: true,
3541
iframeSize: [500, 500]
36-
},
37-
},
42+
}
43+
}
3844
];
3945

4046
const prebidRequests = [
@@ -98,7 +104,7 @@ const prebidRequests = [
98104
skipIframeBusting: false,
99105
sizes: [[300, 250], [300, 300], [250, 250], [600, 50]]
100106
}
101-
},
107+
}
102108
];
103109

104110
const bidderResponse = {
@@ -121,12 +127,12 @@ const bidderResponse = {
121127
};
122128

123129
const setUserAgent = (uaString) => {
124-
window.navigator['__defineGetter__']('userAgent', function () {
130+
window.navigator['__defineGetter__']('userAgent', function() {
125131
return uaString;
126132
});
127133
};
128134

129-
describe('sharethrough internal spec', function () {
135+
describe('sharethrough internal spec', function() {
130136
let windowSpy, windowTopSpy;
131137

132138
beforeEach(function() {
@@ -141,7 +147,7 @@ describe('sharethrough internal spec', function () {
141147
window.top.STR = undefined;
142148
});
143149

144-
describe('we cannot access top level document', function () {
150+
describe('we cannot access top level document', function() {
145151
beforeEach(function() {
146152
window.lockedInFrame = true;
147153
});
@@ -150,27 +156,27 @@ describe('sharethrough internal spec', function () {
150156
window.lockedInFrame = false;
151157
});
152158

153-
it('appends sfp.js to the safeframe', function () {
159+
it('appends sfp.js to the safeframe', function() {
154160
sharethroughInternal.handleIframe();
155161
expect(windowSpy.calledOnce).to.be.true;
156162
});
157163

158-
it('does not append anything if sfp.js is already loaded in the safeframe', function () {
164+
it('does not append anything if sfp.js is already loaded in the safeframe', function() {
159165
window.STR = { Tag: true };
160166
sharethroughInternal.handleIframe();
161167
expect(windowSpy.notCalled).to.be.true;
162168
expect(windowTopSpy.notCalled).to.be.true;
163169
});
164170
});
165171

166-
describe('we are able to bust out of the iframe', function () {
167-
it('appends sfp.js to window.top', function () {
172+
describe('we are able to bust out of the iframe', function() {
173+
it('appends sfp.js to window.top', function() {
168174
sharethroughInternal.handleIframe();
169175
expect(windowSpy.calledOnce).to.be.true;
170176
expect(windowTopSpy.calledOnce).to.be.true;
171177
});
172178

173-
it('only appends sfp-set-targeting.js if sfp.js is already loaded on the page', function () {
179+
it('only appends sfp-set-targeting.js if sfp.js is already loaded on the page', function() {
174180
window.top.STR = { Tag: true };
175181
sharethroughInternal.handleIframe();
176182
expect(windowSpy.calledOnce).to.be.true;
@@ -179,15 +185,15 @@ describe('sharethrough internal spec', function () {
179185
});
180186
});
181187

182-
describe('sharethrough adapter spec', function () {
183-
describe('.code', function () {
184-
it('should return a bidder code of sharethrough', function () {
188+
describe('sharethrough adapter spec', function() {
189+
describe('.code', function() {
190+
it('should return a bidder code of sharethrough', function() {
185191
expect(spec.code).to.eql('sharethrough');
186192
});
187193
});
188194

189-
describe('.isBidRequestValid', function () {
190-
it('should return false if req has no pkey', function () {
195+
describe('.isBidRequestValid', function() {
196+
it('should return false if req has no pkey', function() {
191197
const invalidBidRequest = {
192198
bidder: 'sharethrough',
193199
params: {
@@ -197,7 +203,7 @@ describe('sharethrough adapter spec', function () {
197203
expect(spec.isBidRequestValid(invalidBidRequest)).to.eql(false);
198204
});
199205

200-
it('should return false if req has wrong bidder code', function () {
206+
it('should return false if req has wrong bidder code', function() {
201207
const invalidBidRequest = {
202208
bidder: 'notSharethrough',
203209
params: {
@@ -207,22 +213,22 @@ describe('sharethrough adapter spec', function () {
207213
expect(spec.isBidRequestValid(invalidBidRequest)).to.eql(false);
208214
});
209215

210-
it('should return true if req is correct', function () {
216+
it('should return true if req is correct', function() {
211217
expect(spec.isBidRequestValid(bidRequests[0])).to.eq(true);
212218
expect(spec.isBidRequestValid(bidRequests[1])).to.eq(true);
213-
})
219+
});
214220
});
215221

216-
describe('.buildRequests', function () {
217-
it('should return an array of requests', function () {
222+
describe('.buildRequests', function() {
223+
it('should return an array of requests', function() {
218224
const builtBidRequests = spec.buildRequests(bidRequests);
219225

220226
expect(builtBidRequests[0].url).to.eq('https://btlr.sharethrough.com/WYu2BXv1/v1');
221227
expect(builtBidRequests[1].url).to.eq('https://btlr.sharethrough.com/WYu2BXv1/v1');
222228
expect(builtBidRequests[0].method).to.eq('GET');
223229
});
224230

225-
it('should set the instant_play_capable parameter correctly based on browser userAgent string', function () {
231+
it('should set the instant_play_capable parameter correctly based on browser userAgent string', function() {
226232
setUserAgent('Android Chrome/60');
227233
let builtBidRequests = spec.buildRequests(bidRequests);
228234
expect(builtBidRequests[0].data.instant_play_capable).to.be.true;
@@ -252,51 +258,64 @@ describe('sharethrough adapter spec', function () {
252258
const stub = sinon.stub(sharethroughInternal, 'getProtocol').returns('http:');
253259
const bidRequest = spec.buildRequests(bidRequests, null)[0];
254260
expect(bidRequest.data.secure).to.be.false;
255-
stub.restore()
261+
stub.restore();
256262
});
257263

258264
it('should set the secure parameter to true when the protocol is https', function() {
259265
const stub = sinon.stub(sharethroughInternal, 'getProtocol').returns('https:');
260266
const bidRequest = spec.buildRequests(bidRequests, null)[0];
261267
expect(bidRequest.data.secure).to.be.true;
262-
stub.restore()
268+
stub.restore();
263269
});
264270

265271
it('should set the secure parameter to true when the protocol is neither http or https', function() {
266272
const stub = sinon.stub(sharethroughInternal, 'getProtocol').returns('about:');
267273
const bidRequest = spec.buildRequests(bidRequests, null)[0];
268274
expect(bidRequest.data.secure).to.be.true;
269-
stub.restore()
275+
stub.restore();
270276
});
271277

272-
it('should add ccpa parameter if uspConsent is present', function () {
278+
it('should add ccpa parameter if uspConsent is present', function() {
273279
const uspConsent = '1YNN';
274280
const bidderRequest = { uspConsent: uspConsent };
275281
const bidRequest = spec.buildRequests(bidRequests, bidderRequest)[0];
276282
expect(bidRequest.data.us_privacy).to.eq(uspConsent);
277283
});
278284

279-
it('should add consent parameters if gdprConsent is present', function () {
285+
it('should add consent parameters if gdprConsent is present', function() {
280286
const gdprConsent = { consentString: 'consent_string123', gdprApplies: true };
281287
const bidderRequest = { gdprConsent: gdprConsent };
282288
const bidRequest = spec.buildRequests(bidRequests, bidderRequest)[0];
283289
expect(bidRequest.data.consent_required).to.eq(true);
284290
expect(bidRequest.data.consent_string).to.eq('consent_string123');
285291
});
286292

287-
it('should handle gdprConsent is present but values are undefined case', function () {
293+
it('should handle gdprConsent is present but values are undefined case', function() {
288294
const gdprConsent = { consent_string: undefined, gdprApplies: undefined };
289295
const bidderRequest = { gdprConsent: gdprConsent };
290296
const bidRequest = spec.buildRequests(bidRequests, bidderRequest)[0];
291-
expect(bidRequest.data).to.not.include.any.keys('consent_string')
297+
expect(bidRequest.data).to.not.include.any.keys('consent_string');
292298
});
293299

294-
it('should add the ttduid parameter if a bid request contains a value for Unified ID from The Trade Desk', function () {
300+
it('should add the ttduid parameter if a bid request contains a value for Unified ID from The Trade Desk', function() {
295301
const bidRequest = spec.buildRequests(bidRequests)[0];
296302
expect(bidRequest.data.ttduid).to.eq('fake-tdid');
297303
});
298304

299-
it('should add Sharethrough specific parameters', function () {
305+
it('should add the pubcid parameter if a bid request contains a value for the Publisher Common ID Module in the' +
306+
' userId object of the bidrequest', function() {
307+
const bidRequest = spec.buildRequests(bidRequests)[0];
308+
expect(bidRequest.data.pubcid).to.eq('fake-pubcid');
309+
});
310+
311+
it('should add the pubcid parameter if a bid request contains a value for the Publisher Common ID Module in the' +
312+
' crumbs object of the bidrequest', function() {
313+
const bidRequest = spec.buildRequests(bidRequests)[0];
314+
delete bidRequest.userId;
315+
expect(bidRequest.data.pubcid).to.eq('fake-pubcid');
316+
});
317+
318+
it('should add Sharethrough specific parameters', function() {
300319
const builtBidRequests = spec.buildRequests(bidRequests);
301320
expect(builtBidRequests[0]).to.deep.include({
302321
strData: {
@@ -346,8 +365,8 @@ describe('sharethrough adapter spec', function () {
346365
});
347366
});
348367

349-
describe('.interpretResponse', function () {
350-
it('returns a correctly parsed out response', function () {
368+
describe('.interpretResponse', function() {
369+
it('returns a correctly parsed out response', function() {
351370
expect(spec.interpretResponse(bidderResponse, prebidRequests[0])[0]).to.include(
352371
{
353372
width: 1,
@@ -357,11 +376,11 @@ describe('sharethrough adapter spec', function () {
357376
dealId: 'aDealId',
358377
currency: 'USD',
359378
netRevenue: true,
360-
ttl: 360,
379+
ttl: 360
361380
});
362381
});
363382

364-
it('returns a correctly parsed out response with largest size when strData.skipIframeBusting is true', function () {
383+
it('returns a correctly parsed out response with largest size when strData.skipIframeBusting is true', function() {
365384
expect(spec.interpretResponse(bidderResponse, prebidRequests[1])[0]).to.include(
366385
{
367386
width: 300,
@@ -371,11 +390,11 @@ describe('sharethrough adapter spec', function () {
371390
dealId: 'aDealId',
372391
currency: 'USD',
373392
netRevenue: true,
374-
ttl: 360,
393+
ttl: 360
375394
});
376395
});
377396

378-
it('returns a correctly parsed out response with explicitly defined size when strData.skipIframeBusting is true and strData.iframeSize is provided', function () {
397+
it('returns a correctly parsed out response with explicitly defined size when strData.skipIframeBusting is true and strData.iframeSize is provided', function() {
379398
expect(spec.interpretResponse(bidderResponse, prebidRequests[2])[0]).to.include(
380399
{
381400
width: 500,
@@ -385,11 +404,11 @@ describe('sharethrough adapter spec', function () {
385404
dealId: 'aDealId',
386405
currency: 'USD',
387406
netRevenue: true,
388-
ttl: 360,
407+
ttl: 360
389408
});
390409
});
391410

392-
it('returns a correctly parsed out response with explicitly defined size when strData.skipIframeBusting is false and strData.sizes contains [0, 0] only', function () {
411+
it('returns a correctly parsed out response with explicitly defined size when strData.skipIframeBusting is false and strData.sizes contains [0, 0] only', function() {
393412
expect(spec.interpretResponse(bidderResponse, prebidRequests[3])[0]).to.include(
394413
{
395414
width: 0,
@@ -399,11 +418,11 @@ describe('sharethrough adapter spec', function () {
399418
dealId: 'aDealId',
400419
currency: 'USD',
401420
netRevenue: true,
402-
ttl: 360,
421+
ttl: 360
403422
});
404423
});
405424

406-
it('returns a correctly parsed out response with explicitly defined size when strData.skipIframeBusting is false and strData.sizes contains multiple sizes', function () {
425+
it('returns a correctly parsed out response with explicitly defined size when strData.skipIframeBusting is false and strData.sizes contains multiple sizes', function() {
407426
expect(spec.interpretResponse(bidderResponse, prebidRequests[4])[0]).to.include(
408427
{
409428
width: 300,
@@ -413,26 +432,26 @@ describe('sharethrough adapter spec', function () {
413432
dealId: 'aDealId',
414433
currency: 'USD',
415434
netRevenue: true,
416-
ttl: 360,
435+
ttl: 360
417436
});
418437
});
419438

420-
it('returns a blank array if there are no creatives', function () {
439+
it('returns a blank array if there are no creatives', function() {
421440
const bidResponse = { body: { creatives: [] } };
422441
expect(spec.interpretResponse(bidResponse, prebidRequests[0])).to.be.an('array').that.is.empty;
423442
});
424443

425-
it('returns a blank array if body object is empty', function () {
444+
it('returns a blank array if body object is empty', function() {
426445
const bidResponse = { body: {} };
427446
expect(spec.interpretResponse(bidResponse, prebidRequests[0])).to.be.an('array').that.is.empty;
428447
});
429448

430-
it('returns a blank array if body is null', function () {
449+
it('returns a blank array if body is null', function() {
431450
const bidResponse = { body: null };
432451
expect(spec.interpretResponse(bidResponse, prebidRequests[0])).to.be.an('array').that.is.empty;
433452
});
434453

435-
it('correctly generates ad markup when skipIframeBusting is false', function () {
454+
it('correctly generates ad markup when skipIframeBusting is false', function() {
436455
const adMarkup = spec.interpretResponse(bidderResponse, prebidRequests[0])[0].ad;
437456
let resp = null;
438457

@@ -447,7 +466,7 @@ describe('sharethrough adapter spec', function () {
447466
expect(adMarkup).to.match(/handleIframe/);
448467
});
449468

450-
it('correctly generates ad markup when skipIframeBusting is true', function () {
469+
it('correctly generates ad markup when skipIframeBusting is true', function() {
451470
const adMarkup = spec.interpretResponse(bidderResponse, prebidRequests[1])[0].ad;
452471
let resp = null;
453472

@@ -461,11 +480,11 @@ describe('sharethrough adapter spec', function () {
461480
});
462481
});
463482

464-
describe('.getUserSyncs', function () {
483+
describe('.getUserSyncs', function() {
465484
const cookieSyncs = ['cookieUrl1', 'cookieUrl2', 'cookieUrl3'];
466485
const serverResponses = [{ body: { cookieSyncUrls: cookieSyncs } }];
467486

468-
it('returns an array of correctly formatted user syncs', function () {
487+
it('returns an array of correctly formatted user syncs', function() {
469488
const syncArray = spec.getUserSyncs({ pixelEnabled: true }, serverResponses, null, 'fake-privacy-signal');
470489
expect(syncArray).to.deep.equal([
471490
{ type: 'image', url: 'cookieUrl1&us_privacy=fake-privacy-signal' },
@@ -474,22 +493,22 @@ describe('sharethrough adapter spec', function () {
474493
);
475494
});
476495

477-
it('returns an empty array if serverResponses is empty', function () {
496+
it('returns an empty array if serverResponses is empty', function() {
478497
const syncArray = spec.getUserSyncs({ pixelEnabled: true }, []);
479498
expect(syncArray).to.be.an('array').that.is.empty;
480499
});
481500

482-
it('returns an empty array if the body is null', function () {
501+
it('returns an empty array if the body is null', function() {
483502
const syncArray = spec.getUserSyncs({ pixelEnabled: true }, [{ body: null }]);
484503
expect(syncArray).to.be.an('array').that.is.empty;
485504
});
486505

487-
it('returns an empty array if the body.cookieSyncUrls is missing', function () {
506+
it('returns an empty array if the body.cookieSyncUrls is missing', function() {
488507
const syncArray = spec.getUserSyncs({ pixelEnabled: true }, [{ body: { creatives: ['creative'] } }]);
489508
expect(syncArray).to.be.an('array').that.is.empty;
490509
});
491510

492-
it('returns an empty array if pixels are not enabled', function () {
511+
it('returns an empty array if pixels are not enabled', function() {
493512
const syncArray = spec.getUserSyncs({ pixelEnabled: false }, serverResponses);
494513
expect(syncArray).to.be.an('array').that.is.empty;
495514
});

0 commit comments

Comments
 (0)