Skip to content

Commit

Permalink
SmileWanted : add schain support (prebid#11804)
Browse files Browse the repository at this point in the history
Co-authored-by: QuentinGallard <quentin.gallard@digitalnolimit.com>
  • Loading branch information
2 people authored and DecayConstant committed Jul 18, 2024
1 parent b57d632 commit 0e5d665
Show file tree
Hide file tree
Showing 4 changed files with 245 additions and 23 deletions.
24 changes: 24 additions & 0 deletions libraries/schainSerializer/schainSerializer.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
/**
* Serialize the SupplyChain for Non-OpenRTB Requests
* https://github.com/InteractiveAdvertisingBureau/openrtb/blob/main/supplychainobject.md
*
* @param {Object} schain The supply chain object.
* @param {string} schain.ver The version of the supply chain.
* @param {number} schain.complete Indicates if the chain is complete (1) or not (0).
* @param {Array<Object>} schain.nodes An array of nodes in the supply chain.
* @param {Array<string>} nodesProperties The list of node properties to include in the serialized string.
* Can include: 'asi', 'sid', 'hp', 'rid', 'name', 'domain', 'ext'.
* @returns {string|null} The serialized supply chain string or null if the nodes are not present.
*/
export function serializeSupplyChain(schain, nodesProperties) {
if (!schain?.nodes) return null;

const header = `${schain.ver},${schain.complete}!`;
const nodes = schain.nodes.map(
node => nodesProperties.map(
prop => node[prop] ? encodeURIComponent(node[prop]).replace(/!/g, '%21') : ''
).join(',')
).join('!');

return header + nodes;
}
48 changes: 26 additions & 22 deletions modules/smilewantedBidAdapter.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import {config} from '../src/config.js';
import {registerBidder} from '../src/adapters/bidderFactory.js';
import {BANNER, NATIVE, VIDEO} from '../src/mediaTypes.js';
import {INSTREAM, OUTSTREAM} from '../src/video.js';
import {serializeSupplyChain} from '../libraries/schainSerializer/schainSerializer.js'
import {convertOrtbRequestToProprietaryNative, toOrtbNativeRequest, toLegacyResponse} from '../src/native.js';

const BIDDER_CODE = 'smilewanted';
Expand Down Expand Up @@ -82,7 +83,8 @@ export const spec = {
or from mediaTypes.banner.pos
*/
positionType: bid.params.positionType || '',
prebidVersion: '$prebid.version$'
prebidVersion: '$prebid.version$',
schain: serializeSupplyChain(bid.schain, ['asi', 'sid', 'hp', 'rid', 'name', 'domain', 'ext']),
};

const floor = getBidFloor(bid);
Expand Down Expand Up @@ -154,16 +156,16 @@ export const spec = {
if (response) {
const dealId = response.dealId || '';
const bidResponse = {
requestId: bidRequestData.bidId,
ad: response.ad,
cpm: response.cpm,
width: response.width,
height: response.height,
creativeId: response.creativeId,
dealId: response.dealId,
currency: response.currency,
dealId: response.dealId,
height: response.height,
netRevenue: response.isNetCpm,
requestId: bidRequestData.bidId,
ttl: response.ttl,
ad: response.ad,
width: response.width,
};

if (response.formatTypeSw === 'video_instream' || response.formatTypeSw === 'video_outstream') {
Expand Down Expand Up @@ -209,28 +211,30 @@ export const spec = {
* @param {Object} uspConsent The USP consent parameters
* @return {UserSync[]} The user syncs which should be dropped.
*/
getUserSyncs: function(syncOptions, responses, gdprConsent, uspConsent) {
let params = '';

if (gdprConsent && typeof gdprConsent.consentString === 'string') {
// add 'gdpr' only if 'gdprApplies' is defined
if (typeof gdprConsent.gdprApplies === 'boolean') {
params += `?gdpr=${Number(gdprConsent.gdprApplies)}&gdpr_consent=${gdprConsent.consentString}`;
} else {
params += `?gdpr_consent=${gdprConsent.consentString}`;
getUserSyncs: function (syncOptions, responses, gdprConsent, uspConsent) {
const syncs = [];

if (syncOptions.iframeEnabled) {
let params = [];

if (gdprConsent && typeof gdprConsent.consentString === 'string') {
// add 'gdpr' only if 'gdprApplies' is defined
if (typeof gdprConsent.gdprApplies === 'boolean') {
params.push(`gdpr=${Number(gdprConsent.gdprApplies)}&gdpr_consent=${gdprConsent.consentString}`);
} else {
params.push(`gdpr_consent=${gdprConsent.consentString}`);
}
}
}

if (uspConsent) {
params += `${params ? '&' : '?'}us_privacy=${encodeURIComponent(uspConsent)}`;
}
if (uspConsent) {
params.push(`us_privacy=${encodeURIComponent(uspConsent)}`);
}

const syncs = []
const paramsStr = params.length > 0 ? '?' + params.join('&') : '';

if (syncOptions.iframeEnabled) {
syncs.push({
type: 'iframe',
url: 'https://csync.smilewanted.com' + params
url: 'https://csync.smilewanted.com' + paramsStr
});
}

Expand Down
58 changes: 57 additions & 1 deletion test/spec/modules/smilewantedBidAdapter_spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,49 @@ const DISPLAY_REQUEST_WITH_POSITION_TYPE = [{
},
}];

const SCHAIN = {
'ver': '1.0',
'complete': 1,
'nodes': [
{
'asi': 'exchange1.com',
'sid': '1234',
'hp': 1,
'rid': 'bid-request-1',
'name': 'publisher',
'domain': 'publisher.com'
},
{
'asi': 'exchange2.com',
'sid': 'abcd',
'hp': 1,
'rid': 'bid-request-2',
'name': 'intermediary',
'domain': 'intermediary.com'
}
]
};

const DISPLAY_REQUEST_WITH_SCHAIN = [{
adUnitCode: 'sw_300x250',
bidId: '12345',
sizes: [
[300, 250],
[300, 200]
],
bidder: 'smilewanted',
params: {
zoneId: 1,
},
requestId: 'request_abcd1234',
ortb2Imp: {
ext: {
tid: 'trans_abcd1234',
}
},
schain: SCHAIN,
}];

const BID_RESPONSE_DISPLAY = {
body: {
cpm: 3,
Expand Down Expand Up @@ -580,8 +623,21 @@ describe('smilewantedBidAdapterTests', function () {
expect(requestContent).to.have.property('positionType').and.to.equal('infeed');
});

it('SmileWanted - Verify if schain is well passed', function () {
const request = spec.buildRequests(DISPLAY_REQUEST_WITH_SCHAIN, {});
const requestContent = JSON.parse(request[0].data);
expect(requestContent).to.have.property('schain').and.to.equal('1.0,1!exchange1.com,1234,1,bid-request-1,publisher,publisher.com,!exchange2.com,abcd,1,bid-request-2,intermediary,intermediary.com,');
});

it('SmileWanted - Verify user sync - empty data', function () {
let syncs = spec.getUserSyncs({iframeEnabled: true}, {}, {}, null);
expect(syncs).to.have.lengthOf(1);
expect(syncs[0].type).to.equal('iframe');
expect(syncs[0].url).to.equal('https://csync.smilewanted.com');
});

it('SmileWanted - Verify user sync', function () {
var syncs = spec.getUserSyncs({iframeEnabled: true}, {}, {
let syncs = spec.getUserSyncs({iframeEnabled: true}, {}, {
consentString: 'foo'
}, '1NYN');
expect(syncs).to.have.lengthOf(1);
Expand Down
138 changes: 138 additions & 0 deletions test/spec/schainSerializer/schainSerializer_spec.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,138 @@
import {serializeSupplyChain} from '../../../libraries/schainSerializer/schainSerializer.js'
describe('serializeSupplyChain', () => {
describe('Single Hop - Chain Complete', () => {
it('should serialize a single hop chain with complete information', () => {
const schain = {
ver: '1.0',
complete: 1,
nodes: [
{
asi: 'exchange1.com',
sid: '1234',
hp: 1,
rid: 'bid-request-1',
name: 'publisher',
domain: 'publisher.com'
}
]
};
const nodesProperties = ['asi', 'sid', 'hp', 'rid', 'name', 'domain'];
const expectedResult = '1.0,1!exchange1.com,1234,1,bid-request-1,publisher,publisher.com';
expect(serializeSupplyChain(schain, nodesProperties)).to.equal(expectedResult);
});
});

describe('Single Hop - Chain Complete, optional fields missing', () => {
it('should serialize a single hop chain with missing optional fields', () => {
const schain = {
ver: '1.0',
complete: 1,
nodes: [
{
asi: 'exchange1.com',
sid: '1234',
hp: 1
}
]
};
const nodesProperties = ['asi', 'sid', 'hp', 'rid', 'name', 'domain'];
const expectedResult = '1.0,1!exchange1.com,1234,1,,,';
expect(serializeSupplyChain(schain, nodesProperties)).to.equal(expectedResult);
});
});

describe('Multiple Hops - With all properties supplied', () => {
it('should serialize multiple hops with all properties supplied', () => {
const schain = {
ver: '1.0',
complete: 1,
nodes: [
{
asi: 'exchange1.com',
sid: '1234',
hp: 1,
rid: 'bid-request-1',
name: 'publisher',
domain: 'publisher.com'
},
{
asi: 'exchange2.com',
sid: 'abcd',
hp: 1,
rid: 'bid-request-2',
name: 'intermediary',
domain: 'intermediary.com'
}
]
};
const nodesProperties = ['asi', 'sid', 'hp', 'rid', 'name', 'domain'];
const expectedResult = '1.0,1!exchange1.com,1234,1,bid-request-1,publisher,publisher.com!exchange2.com,abcd,1,bid-request-2,intermediary,intermediary.com';
expect(serializeSupplyChain(schain, nodesProperties)).to.equal(expectedResult);
});
});

describe('Multiple Hops - Chain Complete, optional fields missing', () => {
it('should serialize multiple hops with missing optional fields', () => {
const schain = {
ver: '1.0',
complete: 1,
nodes: [
{
asi: 'exchange1.com',
sid: '1234',
hp: 1
},
{
asi: 'exchange2.com',
sid: 'abcd',
hp: 1
}
]
};
const nodesProperties = ['asi', 'sid', 'hp', 'rid', 'name', 'domain'];
const expectedResult = '1.0,1!exchange1.com,1234,1,,,!exchange2.com,abcd,1,,,';
expect(serializeSupplyChain(schain, nodesProperties)).to.equal(expectedResult);
});
});

describe('Multiple Hops Expected - Chain Incomplete', () => {
it('should serialize multiple hops with chain incomplete', () => {
const schain = {
ver: '1.0',
complete: 0,
nodes: [
{
asi: 'exchange2.com',
sid: 'abcd',
hp: 1
}
]
};
const nodesProperties = ['asi', 'sid', 'hp', 'rid', 'name', 'domain'];
const expectedResult = '1.0,0!exchange2.com,abcd,1,,,';
expect(serializeSupplyChain(schain, nodesProperties)).to.equal(expectedResult);
});
});

describe('Single Hop - Chain Complete, encoded values', () => {
it('should serialize a single hop chain with encoded values', () => {
const schain = {
ver: '1.0',
complete: 1,
nodes: [
{
asi: 'exchange1.com',
sid: '1234!abcd',
hp: 1,
rid: 'bid-request-1',
name: 'publisher, Inc.',
domain: 'publisher.com'
}
]
};
const nodesProperties = ['asi', 'sid', 'hp', 'rid', 'name', 'domain'];
const expectedResult = '1.0,1!exchange1.com,1234%21abcd,1,bid-request-1,publisher%2C%20Inc.,publisher.com';
expect(serializeSupplyChain(schain, nodesProperties)).to.equal(expectedResult);
});
});
});

0 comments on commit 0e5d665

Please sign in to comment.