Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
16 commits
Select commit Hold shift + click to select a range
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
45 changes: 44 additions & 1 deletion modules/smartytechBidAdapter.js
Original file line number Diff line number Diff line change
@@ -1,14 +1,51 @@
import {buildUrl, deepAccess, isArray} from '../src/utils.js'
import {buildUrl, deepAccess, isArray, generateUUID} from '../src/utils.js'
import { BANNER, VIDEO } from '../src/mediaTypes.js';
import {registerBidder} from '../src/adapters/bidderFactory.js';
import {config} from '../src/config.js';
import {chunk} from '../libraries/chunk/chunk.js';
import {getStorageManager} from '../src/storageManager.js';
import {findRootDomain} from '../src/fpd/rootDomain.js';

const BIDDER_CODE = 'smartytech';
export const ENDPOINT_PROTOCOL = 'https';
export const ENDPOINT_DOMAIN = 'server.smartytech.io';
export const ENDPOINT_PATH = '/hb/v2/bidder';

// Alias User ID constants
const AUID_COOKIE_NAME = '_smartytech_auid';
const AUID_COOKIE_EXPIRATION_DAYS = 1825; // 5 years

// Storage manager for cookies
export const storage = getStorageManager({bidderCode: BIDDER_CODE});

/**
* Get or generate Alias User ID (auId)
* - Checks if auId exists in cookie
* - If not, generates new UUID and stores it in cookie on root domain
* @returns {string|null} The alias user ID or null if cookies are not enabled
*/
export function getAliasUserId() {
if (!storage.cookiesAreEnabled()) {
return null;
}

let auId = storage.getCookie(AUID_COOKIE_NAME);

if (auId && auId.length > 0) {
return auId;
}

auId = generateUUID();

const expirationDate = new Date();
expirationDate.setTime(expirationDate.getTime() + (AUID_COOKIE_EXPIRATION_DAYS * 24 * 60 * 60 * 1000));
const expires = expirationDate.toUTCString();

storage.setCookie(AUID_COOKIE_NAME, auId, expires, 'Lax', findRootDomain());

return auId;
}

export const spec = {
supportedMediaTypes: [ BANNER, VIDEO ],
code: BIDDER_CODE,
Expand Down Expand Up @@ -56,6 +93,8 @@ export const spec = {
buildRequests: function (validBidRequests, bidderRequest) {
const referer = bidderRequest?.refererInfo?.page || window.location.href;

const auId = getAliasUserId();

const bidRequests = validBidRequests.map((validBidRequest) => {
const video = deepAccess(validBidRequest, 'mediaTypes.video', false);
const banner = deepAccess(validBidRequest, 'mediaTypes.banner', false);
Expand All @@ -68,6 +107,10 @@ export const spec = {
bidId: validBidRequest.bidId
};

if (auId) {
oneRequest.auId = auId;
}

if (video) {
oneRequest.video = video;

Expand Down
114 changes: 113 additions & 1 deletion test/spec/modules/smartytechBidAdapter_spec.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
import {expect} from 'chai';
import {spec, ENDPOINT_PROTOCOL, ENDPOINT_DOMAIN, ENDPOINT_PATH} from 'modules/smartytechBidAdapter';
import {spec, ENDPOINT_PROTOCOL, ENDPOINT_DOMAIN, ENDPOINT_PATH, getAliasUserId, storage} from 'modules/smartytechBidAdapter';
import {newBidder} from 'src/adapters/bidderFactory.js';
import * as utils from 'src/utils.js';
import sinon from 'sinon';

const BIDDER_CODE = 'smartytech';

Expand Down Expand Up @@ -500,3 +502,113 @@ describe('SmartyTechDSPAdapter: buildRequests with consent data', () => {
});
});
});

describe('SmartyTechDSPAdapter: Alias User ID (auId)', () => {
let cookiesAreEnabledStub;
let getCookieStub;
let setCookieStub;
let generateUUIDStub;

beforeEach(() => {
cookiesAreEnabledStub = sinon.stub(storage, 'cookiesAreEnabled');
getCookieStub = sinon.stub(storage, 'getCookie');
setCookieStub = sinon.stub(storage, 'setCookie');
generateUUIDStub = sinon.stub(utils, 'generateUUID');
});

afterEach(() => {
cookiesAreEnabledStub.restore();
getCookieStub.restore();
setCookieStub.restore();
generateUUIDStub.restore();
});

it('should return null if cookies are not enabled', () => {
cookiesAreEnabledStub.returns(false);
const auId = getAliasUserId();
expect(auId).to.be.null;
});

it('should return existing auId from cookie', () => {
const existingAuId = 'existing-uuid-1234';
cookiesAreEnabledStub.returns(true);
getCookieStub.returns(existingAuId);

const auId = getAliasUserId();
expect(auId).to.equal(existingAuId);
expect(generateUUIDStub.called).to.be.false;
});

it('should generate and store new auId if cookie does not exist', () => {
const newAuId = 'new-uuid-5678';
cookiesAreEnabledStub.returns(true);
getCookieStub.returns(null);
generateUUIDStub.returns(newAuId);

const auId = getAliasUserId();
expect(auId).to.equal(newAuId);
expect(generateUUIDStub.calledOnce).to.be.true;
expect(setCookieStub.calledOnce).to.be.true;

// Check that setCookie was called with correct parameters
const setCookieCall = setCookieStub.getCall(0);
expect(setCookieCall.args[0]).to.equal('_smartytech_auid'); // cookie name
expect(setCookieCall.args[1]).to.equal(newAuId); // cookie value
expect(setCookieCall.args[3]).to.equal('Lax'); // sameSite
});

it('should generate and store new auId if cookie is empty string', () => {
const newAuId = 'new-uuid-9999';
cookiesAreEnabledStub.returns(true);
getCookieStub.returns('');
generateUUIDStub.returns(newAuId);

const auId = getAliasUserId();
expect(auId).to.equal(newAuId);
expect(generateUUIDStub.calledOnce).to.be.true;
});
});

describe('SmartyTechDSPAdapter: buildRequests with auId', () => {
let mockBidRequest;
let mockReferer;
let cookiesAreEnabledStub;
let getCookieStub;

beforeEach(() => {
mockBidRequest = mockBidRequestListData('banner', 2, []);
mockReferer = mockRefererData();
cookiesAreEnabledStub = sinon.stub(storage, 'cookiesAreEnabled');
getCookieStub = sinon.stub(storage, 'getCookie');
});

afterEach(() => {
cookiesAreEnabledStub.restore();
getCookieStub.restore();
});

it('should include auId in bid request when available', () => {
const testAuId = 'test-auid-12345';
cookiesAreEnabledStub.returns(true);
getCookieStub.returns(testAuId);

const request = spec.buildRequests(mockBidRequest, mockReferer);
const data = request.flatMap(resp => resp.data);

data.forEach((req) => {
expect(req).to.have.property('auId');
expect(req.auId).to.equal(testAuId);
});
});

it('should not include auId when cookies are disabled', () => {
cookiesAreEnabledStub.returns(false);

const request = spec.buildRequests(mockBidRequest, mockReferer);
const data = request.flatMap(resp => resp.data);

data.forEach((req) => {
expect(req).to.not.have.property('auId');
});
});
});
Loading