Skip to content

Commit aa65100

Browse files
ksieg-equativeszponderkrzysztofequativjanzych-smartjefftmahoney
authored
Sharethrough Bid Adapter: add cookie sync if Equativ endpoint is called (prebid#13655)
* add support of dsa * restore topics * DSA fix for UT * drafy of adapter * fixes after dev test * make world simpler * fix prev commit * return empty userSyncs array by default * adjustments * apply prettier * unit tests for Equativ adapter * add dsp user sync * add readme * body can be undef * support additional br params * remove user sync * do not send dt param * handle floors and network id * handle empty media types * get min floor * fix desc for u.t. * better name for u.t. * add u.t. for not supported media type * improve currency u.t. * updates after pr review * SADR-6484: initial video setup for new PBJS adapter * SADR-6484: Adding logging requirement missed earlier * SADR-6484: handle ext.rewarded prop for video with new oRTBConverter * SADR-6484: test revision + not sending bid requests where video obj is empty * refactoring and u.t. * rename variable * Equativ: SADR-6615: adding unit tests for and additional logging to bid adapter to support native requests * revert changes rel. to test endpoint * revert changes rel. to test endpoint * split imp[0] into seperate requests and fix u.t. * Equativ bid adapter: adding support for native media type * Equativ bid adapter: update unit test for native-support work * Equativ bid adapter: removing console.log from unit test file * Equativ bid adapter: clarifying refinements regarding native-request processing * Equativ Bid Adapter: updating unit tests for native requests * PR feedback * Equativ Bid Adapter: add audio-specific warnings for missing fields in bid requests * split imp per floor * restore imp id * banner media type may be not set * adapt unit test * remove unnecessary if statement, adapt unit test * remove unnecessary if statement * stx calling eqt endpoint * restore cleanObject logic; fix and add unit tests for multi imp * readd comma * fix linter issues + add unit tests * remove getBidFloor tests * dsp user sync * enable previous auction info * remove logs * send previous auction info * read exp * remove enablePreviousAuctionInfo library * set default ttl in converter config * fix linter * revert test changes * add publisherId param to test eqt endpoint * remove new line * fixes after dev test * Add unit tests * Comment test data. Fix linter issues * Remove test data. Move duplicated code * Fix linter issue * Update calledId * Fix native test * Restore getUserSync. Add UT for equativUtils.js * add prebid version to the request parameters * test update * add renderer * improve u.t. * remove test data * change optional param name * update of u.t * remove empty line * remove semicolon * Add cookie sync for STX adapter. Move cookie sync logic to equativUtils library * Lint * Lint * Fixes after review prebid#13655 --------- Co-authored-by: Elżbieta SZPONDER <eszponder@smartadserver.com> Co-authored-by: eszponder <155961428+eszponder@users.noreply.github.com> Co-authored-by: Krzysztof Sokół <88041828+krzysztofequativ@users.noreply.github.com> Co-authored-by: Krzysztof Sokół <ksokol@smartadserver.com> Co-authored-by: janzych-smart <jzych@smartadserver.com> Co-authored-by: Jeff Mahoney <jeff.t.mahoney@gmail.com> Co-authored-by: Jeff Mahoney <jmahoney@sharethrough.com> Co-authored-by: janzych-smart <103245435+janzych-smart@users.noreply.github.com> Co-authored-by: Elżbieta SZPONDER <eszponder@equativ.com>
1 parent 6e82d0a commit aa65100

6 files changed

Lines changed: 324 additions & 144 deletions

File tree

libraries/equativUtils/equativUtils.js

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import { VIDEO } from '../../src/mediaTypes.js';
22
import { deepAccess, isFn } from '../../src/utils.js';
3+
import { tryAppendQueryString } from '../urlUtils/urlUtils.js';
34

45
const DEFAULT_FLOOR = 0.0;
56

@@ -154,3 +155,46 @@ export function prepareSplitImps(imps, bid, currency, impIdMap, adapter) {
154155

155156
return splitImps;
156157
}
158+
159+
export const COOKIE_SYNC_ORIGIN = 'https://apps.smartadserver.com';
160+
export const COOKIE_SYNC_URL = `${COOKIE_SYNC_ORIGIN}/diff/templates/asset/csync.html`;
161+
export const PID_STORAGE_NAME = 'eqt_pid';
162+
163+
/**
164+
* Handles cookie sync logic
165+
*
166+
* @param {*} syncOptions A sync options object
167+
* @param {*} serverResponses A server responses array
168+
* @param {*} gdprConsent A gdpr consent object
169+
* @param {number} networkId A network id
170+
* @param {*} storage A storage object
171+
* @returns {{type: string, url: string}[]}
172+
*/
173+
export function handleCookieSync(syncOptions, serverResponses, gdprConsent, networkId, storage) {
174+
if (syncOptions.iframeEnabled) {
175+
window.addEventListener('message', function handler(event) {
176+
if (event.origin === COOKIE_SYNC_ORIGIN && event.data.action === 'getConsent') {
177+
if (event.source && event.source.postMessage) {
178+
event.source.postMessage({
179+
action: 'consentResponse',
180+
id: event.data.id,
181+
consents: gdprConsent.vendorData.vendor.consents
182+
}, event.origin);
183+
}
184+
185+
if (event.data.pid) {
186+
storage.setDataInLocalStorage(PID_STORAGE_NAME, event.data.pid);
187+
}
188+
189+
this.removeEventListener('message', handler);
190+
}
191+
});
192+
193+
let url = tryAppendQueryString(COOKIE_SYNC_URL + '?', 'nwid', networkId);
194+
url = tryAppendQueryString(url, 'gdpr', (gdprConsent?.gdprApplies ? '1' : '0'));
195+
196+
return [{ type: 'iframe', url }];
197+
}
198+
199+
return [];
200+
}

modules/equativBidAdapter.js

Lines changed: 13 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
import { ortbConverter } from '../libraries/ortbConverter/converter.js';
2-
import { prepareSplitImps } from '../libraries/equativUtils/equativUtils.js';
3-
import { tryAppendQueryString } from '../libraries/urlUtils/urlUtils.js';
2+
import { handleCookieSync, PID_STORAGE_NAME, prepareSplitImps } from '../libraries/equativUtils/equativUtils.js';
43
import { registerBidder } from '../src/adapters/bidderFactory.js';
54
import { config } from '../src/config.js';
65
import { BANNER, NATIVE, VIDEO } from '../src/mediaTypes.js';
@@ -14,17 +13,14 @@ import { deepAccess, deepSetValue, logError, logWarn, mergeDeep } from '../src/u
1413
*/
1514

1615
const BIDDER_CODE = 'equativ';
17-
const COOKIE_SYNC_ORIGIN = 'https://apps.smartadserver.com';
18-
const COOKIE_SYNC_URL = `${COOKIE_SYNC_ORIGIN}/diff/templates/asset/csync.html`;
1916
const DEFAULT_TTL = 300;
2017
const LOG_PREFIX = 'Equativ:';
2118
const OUTSTREAM_RENDERER_URL = 'https://apps.sascdn.com/diff/video-outstream/equativ-video-outstream.js';
22-
const PID_STORAGE_NAME = 'eqt_pid';
2319

24-
const feedbackArray = [];
25-
const impIdMap = {};
26-
let nwid = 0;
27-
const tokens = {};
20+
let feedbackArray = [];
21+
let impIdMap = {};
22+
let networkId = 0;
23+
let tokens = {};
2824

2925
/**
3026
* Gets value of the local variable impIdMap
@@ -96,7 +92,7 @@ export const spec = {
9692
const requests = [];
9793

9894
bidRequests.forEach(bid => {
99-
const data = converter.toORTB({bidRequests: [bid], bidderRequest});
95+
const data = converter.toORTB({ bidRequests: [bid], bidderRequest });
10096
requests.push({
10197
data,
10298
method: 'POST',
@@ -156,36 +152,12 @@ export const spec = {
156152

157153
/**
158154
* @param syncOptions
155+
* @param serverResponses
156+
* @param gdprConsent
159157
* @returns {{type: string, url: string}[]}
160158
*/
161-
getUserSyncs: (syncOptions, serverResponses, gdprConsent) => {
162-
if (syncOptions.iframeEnabled) {
163-
window.addEventListener('message', function handler(event) {
164-
if (event.origin === COOKIE_SYNC_ORIGIN && event.data.action === 'getConsent') {
165-
if (event.source && event.source.postMessage) {
166-
event.source.postMessage({
167-
action: 'consentResponse',
168-
id: event.data.id,
169-
consents: gdprConsent.vendorData.vendor.consents
170-
}, event.origin);
171-
}
172-
173-
if (event.data.pid) {
174-
storage.setDataInLocalStorage(PID_STORAGE_NAME, event.data.pid);
175-
}
176-
177-
this.removeEventListener('message', handler);
178-
}
179-
});
180-
181-
let url = tryAppendQueryString(COOKIE_SYNC_URL + '?', 'nwid', nwid);
182-
url = tryAppendQueryString(url, 'gdpr', (gdprConsent?.gdprApplies ? '1' : '0'));
183-
184-
return [{ type: 'iframe', url }];
185-
}
186-
187-
return [];
188-
}
159+
getUserSyncs: (syncOptions, serverResponses, gdprConsent) =>
160+
handleCookieSync(syncOptions, serverResponses, gdprConsent, networkId, storage)
189161
};
190162

191163
export const converter = ortbConverter({
@@ -248,9 +220,9 @@ export const converter = ortbConverter({
248220

249221
let req = buildRequest(splitImps, bidderRequest, context);
250222

251-
const env = ['ortb2.site.publisher', 'ortb2.app.publisher', 'ortb2.dooh.publisher'].find(propPath => deepAccess(bid, propPath)) || 'ortb2.site.publisher';
252-
nwid = deepAccess(bid, env + '.id') || bid.params.networkId;
253-
deepSetValue(req, env.replace('ortb2.', '') + '.id', nwid);
223+
let env = ['ortb2.site.publisher', 'ortb2.app.publisher', 'ortb2.dooh.publisher'].find(propPath => deepAccess(bid, propPath)) || 'ortb2.site.publisher';
224+
networkId = deepAccess(bid, env + '.id') || bid.params.networkId;
225+
deepSetValue(req, env.replace('ortb2.', '') + '.id', networkId);
254226

255227
[
256228
{ path: 'mediaTypes.video', props: ['mimes', 'placement'] },

modules/sharethroughBidAdapter.js

Lines changed: 24 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,10 @@
11
import { BANNER, NATIVE, VIDEO } from '../src/mediaTypes.js';
22
import { config } from '../src/config.js';
33
import { ortbConverter } from '../libraries/ortbConverter/converter.js';
4-
import { prepareSplitImps } from '../libraries/equativUtils/equativUtils.js';
4+
import { handleCookieSync, PID_STORAGE_NAME, prepareSplitImps } from '../libraries/equativUtils/equativUtils.js';
55
import { registerBidder } from '../src/adapters/bidderFactory.js';
66
import { deepAccess, generateUUID, inIframe, isPlainObject, logWarn, mergeDeep } from '../src/utils.js';
7+
import { getStorageManager } from '../src/storageManager.js';
78

89
const VERSION = '4.3.0';
910
const BIDDER_CODE = 'sharethrough';
@@ -13,9 +14,12 @@ const EQT_ENDPOINT = 'https://ssb.smartadserver.com/api/bid?callerId=233';
1314
const STR_ENDPOINT = `https://btlr.sharethrough.com/universal/v1?supply_id=${SUPPLY_ID}`;
1415
const IDENTIFIER_PREFIX = 'Sharethrough:';
1516

16-
const impIdMap = {};
17+
let impIdMap = {};
18+
let eqtvNetworkId = 0;
1719
let isEqtvTest = null;
1820

21+
const storage = getStorageManager({ bidderCode: BIDDER_CODE });
22+
1923
/**
2024
* Gets value of the local variable impIdMap
2125
* @returns {*} Value of impIdMap
@@ -94,12 +98,21 @@ export const sharethroughAdapterSpec = {
9498
test: 0,
9599
};
96100

101+
req.user = firstPartyData.user ?? {}
102+
if (!req.user.ext) req.user.ext = {};
103+
req.user.ext.eids = bidRequests[0].userIdAsEids || [];
104+
97105
if (bidRequests[0].params.equativNetworkId) {
98106
isEqtvTest = true;
107+
eqtvNetworkId = bidRequests[0].params.equativNetworkId;
99108
req.site.publisher = {
100109
id: bidRequests[0].params.equativNetworkId,
101110
...req.site.publisher
102111
};
112+
const pid = storage.getDataFromLocalStorage(PID_STORAGE_NAME);
113+
if (pid) {
114+
req.user.buyeruid = pid;
115+
}
103116
}
104117

105118
if (bidderRequest.ortb2?.device?.ext?.cdep) {
@@ -111,10 +124,6 @@ export const sharethroughAdapterSpec = {
111124
mergeDeep(req.device, bidderRequest.ortb2.device);
112125
}
113126

114-
req.user = nullish(firstPartyData.user, {});
115-
if (!req.user.ext) req.user.ext = {};
116-
req.user.ext.eids = bidRequests[0].userIdAsEids || [];
117-
118127
if (bidderRequest.gdprConsent) {
119128
const gdprApplies = bidderRequest.gdprConsent.gdprApplies === true;
120129
req.regs.ext.gdpr = gdprApplies ? 1 : 0;
@@ -190,7 +199,7 @@ export const sharethroughAdapterSpec = {
190199
};
191200

192201
impression.video = {
193-
pos: nullish(videoRequest.pos, 0),
202+
pos: videoRequest.pos ?? 0,
194203
topframe: inIframe() ? 0 : 1,
195204
w,
196205
h,
@@ -327,11 +336,15 @@ export const sharethroughAdapterSpec = {
327336
}
328337
},
329338

330-
getUserSyncs: (syncOptions, serverResponses) => {
331-
const shouldCookieSync =
332-
syncOptions.pixelEnabled && deepAccess(serverResponses, '0.body.cookieSyncUrls') !== undefined;
339+
getUserSyncs: (syncOptions, serverResponses, gdprConsent) => {
340+
if (isEqtvTest) {
341+
return handleCookieSync(syncOptions, serverResponses, gdprConsent, eqtvNetworkId, storage)
342+
} else {
343+
const shouldCookieSync =
344+
syncOptions.pixelEnabled && deepAccess(serverResponses, '0.body.cookieSyncUrls') !== undefined;
333345

334-
return shouldCookieSync ? serverResponses[0].body.cookieSyncUrls.map((url) => ({ type: 'image', url: url })) : [];
346+
return shouldCookieSync ? serverResponses[0].body.cookieSyncUrls.map((url) => ({ type: 'image', url: url })) : [];
347+
}
335348
},
336349

337350
// Empty implementation for prebid core to be able to find it
@@ -363,9 +376,4 @@ function getProtocol() {
363376
return window.location.protocol;
364377
}
365378

366-
// stub for ?? operator
367-
function nullish(input, def) {
368-
return input === null || input === undefined ? def : input;
369-
}
370-
371379
registerBidder(sharethroughAdapterSpec);

test/spec/libraries/equativUtils/equativUtils_spec.js

Lines changed: 143 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import * as equativUtils from "../../../../libraries/equativUtils/equativUtils.js";
2+
import { storage } from "../../../../modules/equativBidAdapter.js";
23

34
describe('equativUtils', () => {
45
describe('prepareSplitImps', () => {
@@ -40,4 +41,146 @@ describe('equativUtils', () => {
4041
expect(result.banner.topframe).to.equal(1);
4142
})
4243
})
44+
45+
describe('handleCookieSync', () => {
46+
let setDataInLocalStorageStub;
47+
let addEventListenerStub;
48+
let messageHandler;
49+
50+
const SAMPLE_RESPONSE = {
51+
body: {
52+
id: '12h712u7-k22g-8124-ab7a-h268s22dy271',
53+
seatbid: [
54+
{
55+
bid: [
56+
{
57+
id: '1bh7jku7-ko2g-8654-ab72-h268shvwy271',
58+
impid: 'r12gwgf231',
59+
price: 0.6565,
60+
adm: '<h1>AD</h1>',
61+
adomain: ['abc.com'],
62+
cid: '1242512',
63+
crid: '535231',
64+
w: 300,
65+
h: 600,
66+
mtype: 1,
67+
cat: ['IAB19', 'IAB19-1'],
68+
cattax: 1,
69+
},
70+
],
71+
seat: '4212',
72+
},
73+
],
74+
cur: 'USD',
75+
statuscode: 0,
76+
},
77+
};
78+
79+
beforeEach(() => {
80+
setDataInLocalStorageStub = sinon.stub(storage, 'setDataInLocalStorage');
81+
addEventListenerStub = sinon.stub(window, 'addEventListener').callsFake((type, handler) => {
82+
if (type === 'message') {
83+
messageHandler = handler;
84+
}
85+
return addEventListenerStub.wrappedMethod.call(this, type, handler);
86+
});
87+
});
88+
afterEach(() => {
89+
setDataInLocalStorageStub.restore();
90+
addEventListenerStub.restore();
91+
});
92+
93+
it('should return empty array if iframe sync not enabled', () => {
94+
const syncs = equativUtils.handleCookieSync({}, SAMPLE_RESPONSE, {}, 73, storage);
95+
expect(syncs).to.deep.equal([]);
96+
});
97+
98+
it('should retrieve and save user pid', (done) => {
99+
equativUtils.handleCookieSync(
100+
{ iframeEnabled: true },
101+
SAMPLE_RESPONSE,
102+
{ gdprApplies: true, vendorData: { vendor: { consents: {} } } },
103+
73,
104+
storage
105+
);
106+
107+
messageHandler.call(window, {
108+
origin: 'https://apps.smartadserver.com',
109+
data: { action: 'getConsent', pid: '7767825890726' },
110+
source: { postMessage: sinon.stub() }
111+
});
112+
113+
expect(setDataInLocalStorageStub.calledOnce).to.be.true;
114+
expect(setDataInLocalStorageStub.calledWith('eqt_pid', '7767825890726')).to.be.true;
115+
done();
116+
});
117+
118+
it('should not save user pid coming from incorrect origin', (done) => {
119+
equativUtils.handleCookieSync(
120+
{ iframeEnabled: true },
121+
SAMPLE_RESPONSE,
122+
{ gdprApplies: true, vendorData: { vendor: { consents: {} } } },
123+
73,
124+
storage
125+
);
126+
127+
messageHandler.call(window, {
128+
origin: 'https://another-origin.com',
129+
data: { action: 'getConsent', pid: '7767825890726' },
130+
source: { postMessage: sinon.stub() }
131+
});
132+
133+
expect(setDataInLocalStorageStub.notCalled).to.be.true;
134+
done();
135+
});
136+
137+
it('should not save empty pid', (done) => {
138+
equativUtils.handleCookieSync(
139+
{ iframeEnabled: true },
140+
SAMPLE_RESPONSE,
141+
{ gdprApplies: true, vendorData: { vendor: { consents: {} } } },
142+
73,
143+
storage
144+
);
145+
146+
messageHandler.call(window, {
147+
origin: 'https://apps.smartadserver.com',
148+
data: { action: 'getConsent', pid: '' },
149+
source: { postMessage: sinon.stub() }
150+
});
151+
152+
expect(setDataInLocalStorageStub.notCalled).to.be.true;
153+
done();
154+
});
155+
156+
it('should return array including iframe cookie sync object (gdprApplies=true)', () => {
157+
const syncs = equativUtils.handleCookieSync(
158+
{ iframeEnabled: true },
159+
SAMPLE_RESPONSE,
160+
{ gdprApplies: true },
161+
73,
162+
storage
163+
);
164+
expect(syncs).to.have.lengthOf(1);
165+
expect(syncs[0]).to.deep.equal({
166+
type: 'iframe',
167+
url: 'https://apps.smartadserver.com/diff/templates/asset/csync.html?nwid=73&gdpr=1&'
168+
});
169+
});
170+
171+
it('should return array including iframe cookie sync object (gdprApplies=false)', () => {
172+
const syncs = equativUtils.handleCookieSync(
173+
{ iframeEnabled: true },
174+
SAMPLE_RESPONSE,
175+
{ gdprApplies: false },
176+
73,
177+
storage
178+
);
179+
expect(syncs).to.have.lengthOf(1);
180+
expect(syncs[0]).to.deep.equal({
181+
type: 'iframe',
182+
url: 'https://apps.smartadserver.com/diff/templates/asset/csync.html?nwid=73&gdpr=0&'
183+
});
184+
});
185+
});
43186
})

0 commit comments

Comments
 (0)