Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
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
51 changes: 45 additions & 6 deletions modules/smartytechBidAdapter.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
import {buildUrl, deepAccess} from '../src/utils.js'
import {buildUrl, deepAccess, isArray} 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';

const BIDDER_CODE = 'smartytech';
export const ENDPOINT_PROTOCOL = 'https';
Expand Down Expand Up @@ -80,20 +82,57 @@ export const spec = {
}
}

// Add user IDs if available
const userIds = deepAccess(validBidRequest, 'userIdAsEids');
if (userIds && isArray(userIds) && userIds.length > 0) {
oneRequest.userIds = userIds;
}

// Add GDPR consent if available
if (bidderRequest && bidderRequest.gdprConsent) {
oneRequest.gdprConsent = {
gdprApplies: bidderRequest.gdprConsent.gdprApplies,
consentString: bidderRequest.gdprConsent.consentString || ''
};

if (bidderRequest.gdprConsent.addtlConsent) {
oneRequest.gdprConsent.addtlConsent = bidderRequest.gdprConsent.addtlConsent;
}
}

// Add CCPA/USP consent if available
if (bidderRequest && bidderRequest.uspConsent) {
oneRequest.uspConsent = bidderRequest.uspConsent;
}

// Add COPPA flag if configured
const coppa = config.getConfig('coppa');
if (coppa) {
oneRequest.coppa = coppa;
}

return oneRequest
});

const adPartnerRequestUrl = buildUrl({
const smartytechRequestUrl = buildUrl({
protocol: ENDPOINT_PROTOCOL,
hostname: ENDPOINT_DOMAIN,
pathname: ENDPOINT_PATH,
});

return {
// Get chunk size from adapter configuration
const adapterSettings = config.getConfig(bidderRequest.bidderCode);
const chunkSize = deepAccess(adapterSettings, 'chunkSize', 10);

// Split bid requests into chunks
const bidChunks = chunk(bidRequests, chunkSize);

// Return array of request objects, one for each chunk
return bidChunks.map(bidChunk => ({
method: 'POST',
url: adPartnerRequestUrl,
data: bidRequests
};
url: smartytechRequestUrl,
data: bidChunk
}));
},

interpretResponse: function (serverResponse, bidRequest) {
Expand Down
175 changes: 158 additions & 17 deletions test/spec/modules/smartytechBidAdapter_spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -182,6 +182,43 @@ function mockRefererData() {
}
}

function mockBidderRequestWithConsents() {
return {
refererInfo: {
page: 'https://some-test.page'
},
gdprConsent: {
gdprApplies: true,
consentString: 'COzTVhaOzTVhaGvAAAENAiCIAP_AAH_AAAAAAEEUACCKAAA',
addtlConsent: '1~1.35.41.101'
},
uspConsent: '1YNN'
}
}

function mockBidRequestWithUserIds(mediaType, size, customSizes) {
const requests = mockBidRequestListData(mediaType, size, customSizes);
return requests.map(request => ({
...request,
userIdAsEids: [
{
source: 'unifiedid.com',
uids: [{
id: 'test-unified-id',
atype: 1
}]
},
{
source: 'pubcid.org',
uids: [{
id: 'test-pubcid',
atype: 1
}]
}
]
}));
}

function mockResponseData(requestData) {
const data = {}
requestData.data.forEach((request, index) => {
Expand Down Expand Up @@ -229,20 +266,25 @@ describe('SmartyTechDSPAdapter: buildRequests', () => {
});
it('correct request URL', () => {
const request = spec.buildRequests(mockBidRequest, mockReferer);
expect(request.url).to.be.equal(`${ENDPOINT_PROTOCOL}://${ENDPOINT_DOMAIN}${ENDPOINT_PATH}`)
request.forEach(req => {
expect(req.url).to.be.equal(`${ENDPOINT_PROTOCOL}://${ENDPOINT_DOMAIN}${ENDPOINT_PATH}`)
});
});
it('correct request method', () => {
const request = spec.buildRequests(mockBidRequest, mockReferer);
expect(request.method).to.be.equal(`POST`)
request.forEach(req => {
expect(req.method).to.be.equal(`POST`)
});
});
it('correct request data', () => {
const data = spec.buildRequests(mockBidRequest, mockReferer).data;
data.forEach((request, index) => {
expect(request.adUnitCode).to.be.equal(mockBidRequest[index].adUnitCode);
expect(request.banner).to.be.equal(mockBidRequest[index].mediaTypes.banner);
expect(request.bidId).to.be.equal(mockBidRequest[index].bidId);
expect(request.endpointId).to.be.equal(mockBidRequest[index].params.endpointId);
expect(request.referer).to.be.equal(mockReferer.refererInfo.page);
const request = spec.buildRequests(mockBidRequest, mockReferer);
const data = request.flatMap(resp => resp.data);
data.forEach((req, index) => {
expect(req.adUnitCode).to.be.equal(mockBidRequest[index].adUnitCode);
expect(req.banner).to.be.equal(mockBidRequest[index].mediaTypes.banner);
expect(req.bidId).to.be.equal(mockBidRequest[index].bidId);
expect(req.endpointId).to.be.equal(mockBidRequest[index].params.endpointId);
expect(req.referer).to.be.equal(mockReferer.refererInfo.page);
})
});
});
Expand All @@ -256,9 +298,10 @@ describe('SmartyTechDSPAdapter: buildRequests banner custom size', () => {
});

it('correct request data', () => {
const data = spec.buildRequests(mockBidRequest, mockReferer).data;
data.forEach((request, index) => {
expect(request.banner.sizes).to.be.equal(mockBidRequest[index].params.sizes);
const request = spec.buildRequests(mockBidRequest, mockReferer);
const data = request.flatMap(resp => resp.data);
data.forEach((req, index) => {
expect(req.banner.sizes).to.be.equal(mockBidRequest[index].params.sizes);
})
});
});
Expand All @@ -272,9 +315,10 @@ describe('SmartyTechDSPAdapter: buildRequests video custom size', () => {
});

it('correct request data', () => {
const data = spec.buildRequests(mockBidRequest, mockReferer).data;
data.forEach((request, index) => {
expect(request.video.sizes).to.be.equal(mockBidRequest[index].params.sizes);
const request = spec.buildRequests(mockBidRequest, mockReferer);
const data = request.flatMap(resp => resp.data);
data.forEach((req, index) => {
expect(req.video.sizes).to.be.equal(mockBidRequest[index].params.sizes);
})
});
});
Expand All @@ -287,7 +331,7 @@ describe('SmartyTechDSPAdapter: interpretResponse', () => {
beforeEach(() => {
const brData = mockBidRequestListData('banner', 2, []);
mockReferer = mockRefererData();
request = spec.buildRequests(brData, mockReferer);
request = spec.buildRequests(brData, mockReferer)[0];
mockBidRequest = {
data: brData
}
Expand Down Expand Up @@ -333,7 +377,7 @@ describe('SmartyTechDSPAdapter: interpretResponse video', () => {
beforeEach(() => {
const brData = mockBidRequestListData('video', 2, []);
mockReferer = mockRefererData();
request = spec.buildRequests(brData, mockReferer);
request = spec.buildRequests(brData, mockReferer)[0];
mockBidRequest = {
data: brData
}
Expand All @@ -359,3 +403,100 @@ describe('SmartyTechDSPAdapter: interpretResponse video', () => {
});
});
});

describe('SmartyTechDSPAdapter: buildRequests with user IDs', () => {
let mockBidRequest;
let mockReferer;
beforeEach(() => {
mockBidRequest = mockBidRequestWithUserIds('banner', 2, []);
mockReferer = mockRefererData();
});

it('should include userIds when available', () => {
const request = spec.buildRequests(mockBidRequest, mockReferer);
const data = request.flatMap(resp => resp.data);

data.forEach((req, index) => {
expect(req).to.have.property('userIds');
expect(req.userIds).to.deep.equal(mockBidRequest[index].userIdAsEids);
});
});

it('should not include userIds when not available', () => {
const bidRequestWithoutUserIds = mockBidRequestListData('banner', 2, []);
const request = spec.buildRequests(bidRequestWithoutUserIds, mockReferer);
const data = request.flatMap(resp => resp.data);

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

it('should not include userIds when userIdAsEids is undefined', () => {
const bidRequestWithUndefinedUserIds = mockBidRequestListData('banner', 2, []).map(req => {
const {userIdAsEids, ...requestWithoutUserIds} = req;
return requestWithoutUserIds;
});
const request = spec.buildRequests(bidRequestWithUndefinedUserIds, mockReferer);
const data = request.flatMap(resp => resp.data);

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

it('should not include userIds when userIdAsEids is empty array', () => {
const bidRequestWithEmptyUserIds = mockBidRequestListData('banner', 2, []).map(req => ({
...req,
userIdAsEids: []
}));
const request = spec.buildRequests(bidRequestWithEmptyUserIds, mockReferer);
const data = request.flatMap(resp => resp.data);

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

describe('SmartyTechDSPAdapter: buildRequests with consent data', () => {
let mockBidRequest;
let mockBidderRequest;
beforeEach(() => {
mockBidRequest = mockBidRequestListData('banner', 2, []);
mockBidderRequest = mockBidderRequestWithConsents();
});

it('should include GDPR consent when available', () => {
const request = spec.buildRequests(mockBidRequest, mockBidderRequest);
const data = request.flatMap(resp => resp.data);

data.forEach((req) => {
expect(req).to.have.property('gdprConsent');
expect(req.gdprConsent.gdprApplies).to.be.true;
expect(req.gdprConsent.consentString).to.equal('COzTVhaOzTVhaGvAAAENAiCIAP_AAH_AAAAAAEEUACCKAAA');
expect(req.gdprConsent.addtlConsent).to.equal('1~1.35.41.101');
});
});

it('should include USP consent when available', () => {
const request = spec.buildRequests(mockBidRequest, mockBidderRequest);
const data = request.flatMap(resp => resp.data);

data.forEach((req) => {
expect(req).to.have.property('uspConsent');
expect(req.uspConsent).to.equal('1YNN');
});
});

it('should not include consent data when not available', () => {
const mockReferer = mockRefererData();
const request = spec.buildRequests(mockBidRequest, mockReferer);
const data = request.flatMap(resp => resp.data);

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