Skip to content

Commit 41dec6e

Browse files
Artem-Maliuhamdusmanalvi
authored andcommitted
Smartytech Bid Adapter: add alias user ID (prebid#13983)
* SmartyTech Bid Adapter: Add userId, consent data support and chunking capability - Add userIdAsEids transmission when available - Add GDPR, CCPA/USP, and COPPA consent support - Add configurable chunking of bid requests to control number of ads per request - Maintain backward compatibility - Add comprehensive test coverage for all new features Results of gulp lint: ✅ No issues Results of gulp test: ✅ All tests pass * retry: trigger CI tests * Empty commit to trigger tests * Empty commit to trigger tests * Smartytech bid adapter: add alias used ID to bid request * SmartyTech Bid Adapter: Add userId, consent data support and chunking capability - Add userIdAsEids transmission when available - Add GDPR, CCPA/USP, and COPPA consent support - Add configurable chunking of bid requests to control number of ads per request - Maintain backward compatibility - Add comprehensive test coverage for all new features Results of gulp lint: ✅ No issues Results of gulp test: ✅ All tests pass * Smartytech bid adapter. Avoid send undefined for gdprApplies and guarantee for config get
1 parent f9da550 commit 41dec6e

File tree

2 files changed

+157
-2
lines changed

2 files changed

+157
-2
lines changed

modules/smartytechBidAdapter.js

Lines changed: 44 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,51 @@
1-
import {buildUrl, deepAccess, isArray} from '../src/utils.js'
1+
import {buildUrl, deepAccess, isArray, generateUUID} from '../src/utils.js'
22
import { BANNER, VIDEO } from '../src/mediaTypes.js';
33
import {registerBidder} from '../src/adapters/bidderFactory.js';
44
import {config} from '../src/config.js';
55
import {chunk} from '../libraries/chunk/chunk.js';
6+
import {getStorageManager} from '../src/storageManager.js';
7+
import {findRootDomain} from '../src/fpd/rootDomain.js';
68

79
const BIDDER_CODE = 'smartytech';
810
export const ENDPOINT_PROTOCOL = 'https';
911
export const ENDPOINT_DOMAIN = 'server.smartytech.io';
1012
export const ENDPOINT_PATH = '/hb/v2/bidder';
1113

14+
// Alias User ID constants
15+
const AUID_COOKIE_NAME = '_smartytech_auid';
16+
const AUID_COOKIE_EXPIRATION_DAYS = 1825; // 5 years
17+
18+
// Storage manager for cookies
19+
export const storage = getStorageManager({bidderCode: BIDDER_CODE});
20+
21+
/**
22+
* Get or generate Alias User ID (auId)
23+
* - Checks if auId exists in cookie
24+
* - If not, generates new UUID and stores it in cookie on root domain
25+
* @returns {string|null} The alias user ID or null if cookies are not enabled
26+
*/
27+
export function getAliasUserId() {
28+
if (!storage.cookiesAreEnabled()) {
29+
return null;
30+
}
31+
32+
let auId = storage.getCookie(AUID_COOKIE_NAME);
33+
34+
if (auId && auId.length > 0) {
35+
return auId;
36+
}
37+
38+
auId = generateUUID();
39+
40+
const expirationDate = new Date();
41+
expirationDate.setTime(expirationDate.getTime() + (AUID_COOKIE_EXPIRATION_DAYS * 24 * 60 * 60 * 1000));
42+
const expires = expirationDate.toUTCString();
43+
44+
storage.setCookie(AUID_COOKIE_NAME, auId, expires, 'Lax', findRootDomain());
45+
46+
return auId;
47+
}
48+
1249
export const spec = {
1350
supportedMediaTypes: [ BANNER, VIDEO ],
1451
code: BIDDER_CODE,
@@ -56,6 +93,8 @@ export const spec = {
5693
buildRequests: function (validBidRequests, bidderRequest) {
5794
const referer = bidderRequest?.refererInfo?.page || window.location.href;
5895

96+
const auId = getAliasUserId();
97+
5998
const bidRequests = validBidRequests.map((validBidRequest) => {
6099
const video = deepAccess(validBidRequest, 'mediaTypes.video', false);
61100
const banner = deepAccess(validBidRequest, 'mediaTypes.banner', false);
@@ -68,6 +107,10 @@ export const spec = {
68107
bidId: validBidRequest.bidId
69108
};
70109

110+
if (auId) {
111+
oneRequest.auId = auId;
112+
}
113+
71114
if (video) {
72115
oneRequest.video = video;
73116

test/spec/modules/smartytechBidAdapter_spec.js

Lines changed: 113 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
import {expect} from 'chai';
2-
import {spec, ENDPOINT_PROTOCOL, ENDPOINT_DOMAIN, ENDPOINT_PATH} from 'modules/smartytechBidAdapter';
2+
import {spec, ENDPOINT_PROTOCOL, ENDPOINT_DOMAIN, ENDPOINT_PATH, getAliasUserId, storage} from 'modules/smartytechBidAdapter';
33
import {newBidder} from 'src/adapters/bidderFactory.js';
4+
import * as utils from 'src/utils.js';
5+
import sinon from 'sinon';
46

57
const BIDDER_CODE = 'smartytech';
68

@@ -500,3 +502,113 @@ describe('SmartyTechDSPAdapter: buildRequests with consent data', () => {
500502
});
501503
});
502504
});
505+
506+
describe('SmartyTechDSPAdapter: Alias User ID (auId)', () => {
507+
let cookiesAreEnabledStub;
508+
let getCookieStub;
509+
let setCookieStub;
510+
let generateUUIDStub;
511+
512+
beforeEach(() => {
513+
cookiesAreEnabledStub = sinon.stub(storage, 'cookiesAreEnabled');
514+
getCookieStub = sinon.stub(storage, 'getCookie');
515+
setCookieStub = sinon.stub(storage, 'setCookie');
516+
generateUUIDStub = sinon.stub(utils, 'generateUUID');
517+
});
518+
519+
afterEach(() => {
520+
cookiesAreEnabledStub.restore();
521+
getCookieStub.restore();
522+
setCookieStub.restore();
523+
generateUUIDStub.restore();
524+
});
525+
526+
it('should return null if cookies are not enabled', () => {
527+
cookiesAreEnabledStub.returns(false);
528+
const auId = getAliasUserId();
529+
expect(auId).to.be.null;
530+
});
531+
532+
it('should return existing auId from cookie', () => {
533+
const existingAuId = 'existing-uuid-1234';
534+
cookiesAreEnabledStub.returns(true);
535+
getCookieStub.returns(existingAuId);
536+
537+
const auId = getAliasUserId();
538+
expect(auId).to.equal(existingAuId);
539+
expect(generateUUIDStub.called).to.be.false;
540+
});
541+
542+
it('should generate and store new auId if cookie does not exist', () => {
543+
const newAuId = 'new-uuid-5678';
544+
cookiesAreEnabledStub.returns(true);
545+
getCookieStub.returns(null);
546+
generateUUIDStub.returns(newAuId);
547+
548+
const auId = getAliasUserId();
549+
expect(auId).to.equal(newAuId);
550+
expect(generateUUIDStub.calledOnce).to.be.true;
551+
expect(setCookieStub.calledOnce).to.be.true;
552+
553+
// Check that setCookie was called with correct parameters
554+
const setCookieCall = setCookieStub.getCall(0);
555+
expect(setCookieCall.args[0]).to.equal('_smartytech_auid'); // cookie name
556+
expect(setCookieCall.args[1]).to.equal(newAuId); // cookie value
557+
expect(setCookieCall.args[3]).to.equal('Lax'); // sameSite
558+
});
559+
560+
it('should generate and store new auId if cookie is empty string', () => {
561+
const newAuId = 'new-uuid-9999';
562+
cookiesAreEnabledStub.returns(true);
563+
getCookieStub.returns('');
564+
generateUUIDStub.returns(newAuId);
565+
566+
const auId = getAliasUserId();
567+
expect(auId).to.equal(newAuId);
568+
expect(generateUUIDStub.calledOnce).to.be.true;
569+
});
570+
});
571+
572+
describe('SmartyTechDSPAdapter: buildRequests with auId', () => {
573+
let mockBidRequest;
574+
let mockReferer;
575+
let cookiesAreEnabledStub;
576+
let getCookieStub;
577+
578+
beforeEach(() => {
579+
mockBidRequest = mockBidRequestListData('banner', 2, []);
580+
mockReferer = mockRefererData();
581+
cookiesAreEnabledStub = sinon.stub(storage, 'cookiesAreEnabled');
582+
getCookieStub = sinon.stub(storage, 'getCookie');
583+
});
584+
585+
afterEach(() => {
586+
cookiesAreEnabledStub.restore();
587+
getCookieStub.restore();
588+
});
589+
590+
it('should include auId in bid request when available', () => {
591+
const testAuId = 'test-auid-12345';
592+
cookiesAreEnabledStub.returns(true);
593+
getCookieStub.returns(testAuId);
594+
595+
const request = spec.buildRequests(mockBidRequest, mockReferer);
596+
const data = request.flatMap(resp => resp.data);
597+
598+
data.forEach((req) => {
599+
expect(req).to.have.property('auId');
600+
expect(req.auId).to.equal(testAuId);
601+
});
602+
});
603+
604+
it('should not include auId when cookies are disabled', () => {
605+
cookiesAreEnabledStub.returns(false);
606+
607+
const request = spec.buildRequests(mockBidRequest, mockReferer);
608+
const data = request.flatMap(resp => resp.data);
609+
610+
data.forEach((req) => {
611+
expect(req).to.not.have.property('auId');
612+
});
613+
});
614+
});

0 commit comments

Comments
 (0)