Skip to content

Commit 4a8390e

Browse files
IlliaMilCopilot
andauthored
Start.io: Create User ID submodule and improve adapter (prebid#14583)
* Start.io - Create User ID submodule and improve adapter * Update modules/startioIdSystem.md Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> * Fix: Adjust spacing formatting in getStorageManager parameters * Fix: Adjust spacing formatting in getStorageManager parameters (#2) * Refactor: Remove unused storage management logic from StartIO ID module and set default storage expiration * Refactor: Remove unused storage management logic from StartIO ID module and set default storage expiration * Enhance: Add configurable storage options and expiration to StartIO ID module * Docs: Update StartIO ID module documentation with storage configuration details * Docs: Update StartIO ID module documentation with storage configuration details --------- Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
1 parent b6c261c commit 4a8390e

9 files changed

Lines changed: 486 additions & 5 deletions

File tree

modules/.submodules.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@
5151
"pubmaticIdSystem",
5252
"rewardedInterestIdSystem",
5353
"sharedIdSystem",
54+
"startioIdSystem",
5455
"taboolaIdSystem",
5556
"tapadIdSystem",
5657
"teadsIdSystem",

modules/startioBidAdapter.js

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,15 @@
11
import { registerBidder } from '../src/adapters/bidderFactory.js';
22
import { BANNER, VIDEO, NATIVE } from '../src/mediaTypes.js';
3-
import { logError, isFn, isPlainObject } from '../src/utils.js';
3+
import { logError, isFn, isPlainObject, formatQS } from '../src/utils.js';
44
import { ortbConverter } from '../libraries/ortbConverter/converter.js'
55
import { ortb25Translator } from '../libraries/ortb2.5Translator/translator.js';
6+
import { getUserSyncParams } from '../libraries/userSyncUtils/userSyncUtils.js';
67

78
const BIDDER_CODE = 'startio';
89
const METHOD = 'POST';
910
const GVLID = 1216;
1011
const ENDPOINT_URL = `https://pbc-rtb.startappnetwork.com/1.3/2.5/getbid?account=pbc`;
12+
const IFRAME_URL = 'https://cs.startappnetwork.com/sync?p=m4b8b3y4';
1113

1214
const converter = ortbConverter({
1315
imp(buildImp, bidRequest, context) {
@@ -151,6 +153,23 @@ export const spec = {
151153
},
152154

153155
onSetTargeting: (bid) => { },
156+
157+
getUserSyncs: function(syncOptions, serverResponses, gdprConsent, uspConsent, gppConsent) {
158+
const syncs = [];
159+
160+
if (syncOptions.iframeEnabled) {
161+
const consentParams = getUserSyncParams(gdprConsent, uspConsent, gppConsent);
162+
const queryString = formatQS(consentParams);
163+
const queryParam = queryString ? `&${queryString}` : '';
164+
165+
syncs.push({
166+
type: 'iframe',
167+
url: `${IFRAME_URL}${queryParam}`
168+
});
169+
}
170+
171+
return syncs;
172+
}
154173
};
155174

156175
registerBidder(spec);

modules/startioBidAdapter.md

Lines changed: 28 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ var adUnits = [
2525
bidder: 'startio',
2626
params: {
2727
// REQUIRED - Publisher Account ID
28-
accountId: 'your-account-id',
28+
publisherId: 'your-account-id',
2929
3030
// OPTIONAL - Enable test ads
3131
testAdsEnabled: true
@@ -58,7 +58,7 @@ var videoAdUnits = [
5858
{
5959
bidder: 'startio',
6060
params: {
61-
accountId: 'your-account-id',
61+
publisherId: 'your-account-id',
6262
testAdsEnabled: true
6363
}
6464
}
@@ -85,7 +85,7 @@ var nativeAdUnits = [
8585
{
8686
bidder: 'startio',
8787
params: {
88-
accountId: 'your-account-id',
88+
publisherId: 'your-account-id',
8989
testAdsEnabled: true
9090
}
9191
}
@@ -94,8 +94,32 @@ var nativeAdUnits = [
9494
];
9595
```
9696

97+
### Prebid Params Enabling User Sync
98+
99+
To enable iframe-based user syncing for Start.io, include the `filterSettings` configuration in your `userSync` setup:
100+
101+
```javascript
102+
pbjs.setConfig({
103+
userSync: {
104+
userIds: [{
105+
name: 'startioId',
106+
storage: {
107+
type: 'cookie&html5',
108+
name: 'startioId'
109+
}
110+
}],
111+
filterSettings: {
112+
iframe: {
113+
bidders: ['startio'],
114+
filter: 'include'
115+
}
116+
}
117+
}
118+
});
119+
```
120+
97121
# Additional Notes
98122
- The adapter processes requests via OpenRTB 2.5 standards.
99-
- Ensure that the `accountId` parameter is set correctly for your integration.
123+
- Ensure that the `publisherId` parameter is set correctly for your integration.
100124
- Test ads can be enabled using `testAdsEnabled: true` during development.
101125
- The adapter supports multiple ad formats, allowing publishers to serve banners, native ads and instream video ads seamlessly.

modules/startioIdSystem.js

Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
/**
2+
* This module adds startio ID support to the User ID module
3+
* The {@link module:modules/userId} module is required
4+
* @module modules/startioIdSystem
5+
* @requires module:modules/userId
6+
*/
7+
import { logError, formatQS } from '../src/utils.js';
8+
import { submodule } from '../src/hook.js';
9+
import { ajax } from '../src/ajax.js';
10+
import { getUserSyncParams } from '../libraries/userSyncUtils/userSyncUtils.js';
11+
12+
const MODULE_NAME = 'startioId';
13+
const GVLID = 1216;
14+
const DEFAULT_ENDPOINT = 'https://cs.startappnetwork.com/get-uid-obj?p=m4b8b3y4';
15+
16+
function fetchIdFromServer(callback, consentData) {
17+
const consentParams = getUserSyncParams(
18+
consentData?.gdpr,
19+
consentData?.usp,
20+
consentData?.gpp
21+
);
22+
const queryString = formatQS(consentParams);
23+
const url = queryString ? `${DEFAULT_ENDPOINT}&${queryString}` : DEFAULT_ENDPOINT;
24+
25+
const callbacks = {
26+
success: response => {
27+
let responseId;
28+
try {
29+
const responseObj = JSON.parse(response);
30+
if (responseObj && responseObj.uid) {
31+
responseId = responseObj.uid;
32+
} else {
33+
logError(`${MODULE_NAME}: Server response missing 'uid' field`);
34+
}
35+
} catch (error) {
36+
logError(`${MODULE_NAME}: Error parsing server response`, error);
37+
}
38+
callback(responseId);
39+
},
40+
error: error => {
41+
logError(`${MODULE_NAME}: ID fetch encountered an error`, error);
42+
callback();
43+
}
44+
};
45+
ajax(url, callbacks, undefined, { method: 'GET', withCredentials: true });
46+
}
47+
48+
export const startioIdSubmodule = {
49+
name: MODULE_NAME,
50+
gvlid: GVLID,
51+
decode(value) {
52+
return value && typeof value === 'string'
53+
? { 'startioId': value }
54+
: undefined;
55+
},
56+
getId(config, consentData, storedId) {
57+
if (storedId) {
58+
return { id: storedId };
59+
}
60+
if (config.storage && config.storage.expires == null) {
61+
config.storage.expires = 90;
62+
}
63+
return { callback: (cb) => fetchIdFromServer(cb, consentData) };
64+
},
65+
66+
eids: {
67+
'startioId': {
68+
source: 'start.io',
69+
atype: 1
70+
},
71+
}
72+
};
73+
74+
submodule('userId', startioIdSubmodule);

modules/startioIdSystem.md

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
## Start.io User ID Submodule
2+
3+
The Start.io User ID submodule generates and persists a unique user identifier by fetching it from a Start.io-managed endpoint. This endpoint is fixed within the submodule implementation and is not configurable via Prebid.js parameters. The ID is stored in both cookies and local storage for subsequent page loads and is made available to other Prebid.js modules via the standard `eids` interface.
4+
5+
For integration support, contact prebid@start.io.
6+
7+
### Prebid Params Enabling User Sync
8+
9+
To enable iframe-based user syncing for Start.io, include the `filterSettings` configuration in your `userSync` setup:
10+
11+
```javascript
12+
pbjs.setConfig({
13+
userSync: {
14+
userIds: [{
15+
name: 'startioId',
16+
storage: {
17+
type: 'cookie&html5', // 'cookie', 'html5', or 'cookie&html5'
18+
name: 'startioId',
19+
expires: 90 // optional, 90 days by default
20+
}
21+
}],
22+
filterSettings: {
23+
iframe: {
24+
bidders: ['startio'],
25+
filter: 'include'
26+
}
27+
}
28+
}
29+
});
30+
```
31+
32+
This configuration allows Start.io to sync user data via iframe, which is necessary for cross-domain user identification.
33+
34+
## Parameter Descriptions for the `userSync` Configuration Section
35+
36+
The below parameters apply only to the Start.io User ID integration.
37+
38+
| Param under userSync.userIds[] | Scope | Type | Description | Example |
39+
| --- | --- | --- | --- | --- |
40+
| name | Required | String | The name of this module. | `"startioId"` |
41+
| storage | Required | Object | Storage configuration for the user ID. | |
42+
| storage.type | Required | String | Type of storage: `"cookie"`, `"html5"`, or `"cookie&html5"`. | `"cookie&html5"` |
43+
| storage.name | Required | String | The name used to store the user ID. | `"startioId"` |
44+
| storage.expires | Optional | Number | Number of days before the stored ID expires. Defaults to `90`. | `365` |

modules/userId/eids.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -350,6 +350,13 @@ userIdAsEids = [
350350
id: 'some-random-id-value',
351351
atype: 1
352352
}]
353+
},
354+
{
355+
source: 'start.io',
356+
uids: [{
357+
id: 'some-random-id-value',
358+
atype: 1
359+
}]
353360
}
354361
]
355362
```

modules/userId/userId.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -157,6 +157,9 @@ pbjs.setConfig({
157157
},
158158
{
159159
name: "mygaruId"
160+
},
161+
{
162+
name: "startioId"
160163
}
161164
],
162165
syncDelay: 5000,

test/spec/modules/startioBidAdapter_spec.js

Lines changed: 94 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -370,4 +370,98 @@ describe('Prebid Adapter: Startio', function () {
370370
});
371371
}
372372
});
373+
374+
describe('getUserSyncs', function () {
375+
it('should return an iframe sync when iframeEnabled is true', function () {
376+
const syncs = spec.getUserSyncs({ iframeEnabled: true }, []);
377+
378+
expect(syncs).to.have.lengthOf(1);
379+
expect(syncs[0].type).to.equal('iframe');
380+
expect(syncs[0].url).to.be.a('string');
381+
});
382+
383+
it('should return an empty array when iframeEnabled is false', function () {
384+
const syncs = spec.getUserSyncs({ iframeEnabled: false }, []);
385+
386+
expect(syncs).to.have.lengthOf(0);
387+
});
388+
389+
it('should return an empty array when syncOptions is empty', function () {
390+
const syncs = spec.getUserSyncs({}, []);
391+
392+
expect(syncs).to.have.lengthOf(0);
393+
});
394+
395+
it('should append GDPR consent params to the sync URL', function () {
396+
const gdprConsent = {
397+
gdprApplies: true,
398+
consentString: 'BOJ/P2HOJ/P2HABABMAAAAAZ+A=='
399+
};
400+
401+
const syncs = spec.getUserSyncs({ iframeEnabled: true }, [], gdprConsent);
402+
403+
expect(syncs).to.have.lengthOf(1);
404+
expect(syncs[0].url).to.include('gdpr=1');
405+
expect(syncs[0].url).to.include('gdpr_consent=BOJ/P2HOJ/P2HABABMAAAAAZ+A==');
406+
});
407+
408+
it('should append gdpr=0 when gdprApplies is false', function () {
409+
const gdprConsent = {
410+
gdprApplies: false,
411+
consentString: ''
412+
};
413+
414+
const syncs = spec.getUserSyncs({ iframeEnabled: true }, [], gdprConsent);
415+
416+
expect(syncs[0].url).to.include('gdpr=0');
417+
});
418+
419+
it('should append USP consent param to the sync URL', function () {
420+
const syncs = spec.getUserSyncs({ iframeEnabled: true }, [], undefined, '1YNN');
421+
422+
expect(syncs).to.have.lengthOf(1);
423+
expect(syncs[0].url).to.include('us_privacy=1YNN');
424+
});
425+
426+
it('should append GPP consent params to the sync URL', function () {
427+
const gppConsent = {
428+
gppString: 'DBABMA~BAAAAAAAAgA.QA',
429+
applicableSections: [7, 8]
430+
};
431+
432+
const syncs = spec.getUserSyncs({ iframeEnabled: true }, [], undefined, undefined, gppConsent);
433+
434+
expect(syncs).to.have.lengthOf(1);
435+
expect(syncs[0].url).to.include('gpp=DBABMA~BAAAAAAAAgA.QA');
436+
expect(syncs[0].url).to.include('gpp_sid=7,8');
437+
});
438+
439+
it('should append all consent params together when all are provided', function () {
440+
const gdprConsent = {
441+
gdprApplies: true,
442+
consentString: 'testConsent'
443+
};
444+
const uspConsent = '1YNN';
445+
const gppConsent = {
446+
gppString: 'testGpp',
447+
applicableSections: [2]
448+
};
449+
450+
const syncs = spec.getUserSyncs({ iframeEnabled: true }, [], gdprConsent, uspConsent, gppConsent);
451+
452+
expect(syncs).to.have.lengthOf(1);
453+
expect(syncs[0].url).to.include('gdpr=1');
454+
expect(syncs[0].url).to.include('gdpr_consent=testConsent');
455+
expect(syncs[0].url).to.include('us_privacy=1YNN');
456+
expect(syncs[0].url).to.include('gpp=testGpp');
457+
expect(syncs[0].url).to.include('gpp_sid=2');
458+
});
459+
460+
it('should not append query string when no consent params are provided', function () {
461+
const syncs = spec.getUserSyncs({ iframeEnabled: true }, []);
462+
463+
expect(syncs).to.have.lengthOf(1);
464+
expect(syncs[0].url).to.equal('https://cs.startappnetwork.com/sync?p=m4b8b3y4');
465+
});
466+
});
373467
});

0 commit comments

Comments
 (0)