Skip to content

Commit 99982ac

Browse files
authored
feat: added only rokt, all, none click ids when collectClickIdV2Enabl… (#1062)
1 parent 645f46a commit 99982ac

12 files changed

+322
-126
lines changed

src/batchUploader.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -173,9 +173,12 @@ export class BatchUploader {
173173

174174
let customFlags: SDKEventCustomFlags = {...event.CustomFlags};
175175
let integrationAttributes: IntegrationAttributes = _Store.integrationAttributes;
176+
const integrationSpecificIds = getFeatureFlag(Constants.FeatureFlags.CaptureIntegrationSpecificIds) as boolean;
177+
const integrationSpecificIdsV2 = getFeatureFlag(Constants.FeatureFlags.CaptureIntegrationSpecificIdsV2) as string || '';
178+
const isIntegrationCaptureEnabled = (integrationSpecificIdsV2 && integrationSpecificIdsV2 !== Constants.CaptureIntegrationSpecificIdsV2Modes.None) || integrationSpecificIds === true;
176179

177180
// https://go.mparticle.com/work/SQDSDKS-5053
178-
if (getFeatureFlag && getFeatureFlag(Constants.FeatureFlags.CaptureIntegrationSpecificIds)) {
181+
if (isIntegrationCaptureEnabled) {
179182

180183
// Attempt to recapture click IDs in case a third party integration
181184
// has added or updated new click IDs since the last event was sent.

src/constants.ts

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -184,7 +184,15 @@ const Constants = {
184184
DirectUrlRouting: 'directURLRouting',
185185
CacheIdentity: 'cacheIdentity',
186186
AudienceAPI: 'audienceAPI',
187+
// CaptureIntegrationSpecificIds (legacy): boolean flag from server/UI
188+
// - 'True' → capture all integration-specific IDs
189+
// - 'False' → capture none
187190
CaptureIntegrationSpecificIds: 'captureIntegrationSpecificIds',
191+
// CaptureIntegrationSpecificIdsV2 (new): string mode from server
192+
// - 'all' → capture all IDs
193+
// - 'none' → capture none
194+
// - 'roktonly' → capture only Rokt-related IDs
195+
CaptureIntegrationSpecificIdsV2: 'captureIntegrationSpecificIdsV2',
188196
AstBackgroundEvents: 'astBackgroundEvents',
189197
},
190198
DefaultInstance: 'default_instance',
@@ -200,6 +208,11 @@ const Constants = {
200208
Development: 'development',
201209
Production: 'production',
202210
},
211+
CaptureIntegrationSpecificIdsV2Modes: {
212+
All: 'all',
213+
None: 'none',
214+
RoktOnly: 'roktonly',
215+
},
203216
} as const;
204217

205218
export default Constants;

src/integrationCapture.ts

Lines changed: 61 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import { SDKEventCustomFlags } from './sdkRuntimeModels';
2+
import Constants from './constants';
23
import { IntegrationAttributes } from './store';
34
import {
45
Dictionary,
@@ -73,7 +74,7 @@ interface IntegrationIdMapping {
7374
[key: string]: IntegrationMappingItem;
7475
}
7576

76-
const integrationMapping: IntegrationIdMapping = {
77+
const integrationMappingExternal: IntegrationIdMapping = {
7778
// Facebook / Meta
7879
fbclid: {
7980
mappedKey: 'Facebook.ClickId',
@@ -103,6 +104,25 @@ const integrationMapping: IntegrationIdMapping = {
103104
output: IntegrationOutputs.CUSTOM_FLAGS,
104105
},
105106

107+
// TIKTOK
108+
ttclid: {
109+
mappedKey: 'TikTok.Callback',
110+
output: IntegrationOutputs.CUSTOM_FLAGS,
111+
},
112+
_ttp: {
113+
mappedKey: 'tiktok_cookie_id',
114+
output: IntegrationOutputs.PARTNER_IDENTITIES,
115+
},
116+
117+
// Snapchat
118+
// https://businesshelp.snapchat.com/s/article/troubleshooting-click-id?language=en_US
119+
ScCid: {
120+
mappedKey: 'SnapchatConversions.ClickId',
121+
output: IntegrationOutputs.CUSTOM_FLAGS,
122+
},
123+
};
124+
125+
const integrationMappingRokt: IntegrationIdMapping = {
106126
// Rokt
107127
// https://docs.rokt.com/developers/integration-guides/web/advanced/rokt-id-tag/
108128
// https://go.mparticle.com/work/SQDSDKS-7167
@@ -121,23 +141,6 @@ const integrationMapping: IntegrationIdMapping = {
121141
output: IntegrationOutputs.INTEGRATION_ATTRIBUTES,
122142
moduleId: 1277,
123143
},
124-
125-
// TIKTOK
126-
ttclid: {
127-
mappedKey: 'TikTok.Callback',
128-
output: IntegrationOutputs.CUSTOM_FLAGS,
129-
},
130-
_ttp: {
131-
mappedKey: 'tiktok_cookie_id',
132-
output: IntegrationOutputs.PARTNER_IDENTITIES,
133-
},
134-
135-
// Snapchat
136-
// https://businesshelp.snapchat.com/s/article/troubleshooting-click-id?language=en_US
137-
ScCid: {
138-
mappedKey: 'SnapchatConversions.ClickId',
139-
output: IntegrationOutputs.CUSTOM_FLAGS,
140-
},
141144
};
142145

143146
export default class IntegrationCapture {
@@ -146,9 +149,11 @@ export default class IntegrationCapture {
146149
public readonly filteredPartnerIdentityMappings: IntegrationIdMapping;
147150
public readonly filteredCustomFlagMappings: IntegrationIdMapping;
148151
public readonly filteredIntegrationAttributeMappings: IntegrationIdMapping;
152+
public captureMode?: valueof<typeof Constants.CaptureIntegrationSpecificIdsV2Modes>;
149153

150-
constructor() {
154+
constructor(captureMode: valueof<typeof Constants.CaptureIntegrationSpecificIdsV2Modes>) {
151155
this.initialTimestamp = Date.now();
156+
this.captureMode = captureMode;
152157

153158
// Cache filtered mappings for faster access
154159
this.filteredPartnerIdentityMappings = this.filterMappings(IntegrationOutputs.PARTNER_IDENTITIES);
@@ -205,7 +210,8 @@ export default class IntegrationCapture {
205210
* Captures cookies based on the integration ID mapping.
206211
*/
207212
public captureCookies(): Dictionary<string> {
208-
const cookies = getCookies(Object.keys(integrationMapping));
213+
const integrationKeys = this.getAllowedKeysForMode();
214+
const cookies = getCookies(integrationKeys);
209215
return this.applyProcessors(cookies, getHref(), this.initialTimestamp);
210216
}
211217

@@ -221,8 +227,9 @@ export default class IntegrationCapture {
221227
* Captures local storage based on the integration ID mapping.
222228
*/
223229
public captureLocalStorage(): Dictionary<string> {
230+
const integrationKeys = this.getAllowedKeysForMode();
224231
let localStorageItems: Dictionary<string> = {};
225-
for (const key in integrationMapping) {
232+
for (const key of integrationKeys) {
226233
const localStorageItem = localStorage.getItem(key);
227234
if (localStorageItem) {
228235
localStorageItems[key] = localStorageItem;
@@ -237,7 +244,8 @@ export default class IntegrationCapture {
237244
* @returns {Dictionary<string>} The query parameters.
238245
*/
239246
public getQueryParams(): Dictionary<string> {
240-
return queryStringParser(getHref(), Object.keys(integrationMapping));
247+
const integrationKeys = this.getAllowedKeysForMode();
248+
return queryStringParser(getHref(), integrationKeys);
241249
}
242250

243251
/**
@@ -316,27 +324,52 @@ export default class IntegrationCapture {
316324
timestamp?: number
317325
): Dictionary<string> {
318326
const processedClickIds: Dictionary<string> = {};
319-
327+
const integrationKeys = this.getActiveIntegrationMapping();
328+
320329
for (const key in clickIds) {
321330
if (clickIds.hasOwnProperty(key)) {
322331
const value = clickIds[key];
323-
const processor = integrationMapping[key]?.processor;
332+
const processor = integrationKeys[key]?.processor;
324333
processedClickIds[key] = processor ? processor(value, url, timestamp) : value;
325334
}
326335
}
327-
328336
return processedClickIds;
329337
}
330338

331339
private filterMappings(
332340
outputType: valueof<typeof IntegrationOutputs>
333341
): IntegrationIdMapping {
334342
const filteredMappings: IntegrationIdMapping = {};
335-
for (const key in integrationMapping) {
336-
if (integrationMapping[key].output === outputType) {
337-
filteredMappings[key] = integrationMapping[key];
343+
const integrationKeys = this.getActiveIntegrationMapping();
344+
for (const key in integrationKeys) {
345+
if (integrationKeys[key].output === outputType) {
346+
filteredMappings[key] = integrationKeys[key];
338347
}
339348
}
340349
return filteredMappings;
341350
}
351+
352+
/**
353+
* Returns the allowed keys to capture based on the current mode.
354+
* For RoktOnly, limit capture to Rokt keys; for All, capture all mapped keys.
355+
*/
356+
private getAllowedKeysForMode(): string[] {
357+
return Object.keys(this.getActiveIntegrationMapping());
358+
}
359+
360+
/**
361+
* Selects the active integration mapping for the current captureMode.
362+
* - 'roktonly': only Rokt IDs are considered
363+
* - 'all': both External and Rokt IDs are considered
364+
* - else: returns an empty mapping and nothing will be captured
365+
*/
366+
private getActiveIntegrationMapping(): IntegrationIdMapping {
367+
if (this.captureMode === Constants.CaptureIntegrationSpecificIdsV2Modes.RoktOnly) {
368+
return integrationMappingRokt;
369+
}
370+
if (this.captureMode === Constants.CaptureIntegrationSpecificIdsV2Modes.All) {
371+
return { ...integrationMappingExternal, ...integrationMappingRokt };
372+
}
373+
return {};
374+
}
342375
}

src/mp-instance.ts

Lines changed: 18 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ import Consent, { IConsent } from './consent';
3636
import KitBlocker from './kitBlocking';
3737
import ConfigAPIClient, { IKitConfigs } from './configAPIClient';
3838
import IdentityAPIClient from './identityApiClient';
39-
import { isFunction, parseConfig } from './utils';
39+
import { isFunction, parseConfig, valueof } from './utils';
4040
import { LocalStorageVault } from './vault';
4141
import { removeExpiredIdentityCacheDates } from './identity-utils';
4242
import IntegrationCapture from './integrationCapture';
@@ -91,8 +91,8 @@ export interface IMParticleWebSDKInstance extends MParticleWebSDK {
9191
_timeOnSiteTimer: ForegroundTimer;
9292
}
9393

94-
const { Messages, HTTPCodes, FeatureFlags } = Constants;
95-
const { ReportBatching, CaptureIntegrationSpecificIds } = FeatureFlags;
94+
const { Messages, HTTPCodes, FeatureFlags, CaptureIntegrationSpecificIdsV2Modes } = Constants;
95+
const { ReportBatching, CaptureIntegrationSpecificIds, CaptureIntegrationSpecificIdsV2 } = FeatureFlags;
9696
const { StartingInitialization } = Messages.InformationMessages;
9797

9898
/**
@@ -128,7 +128,6 @@ export default function mParticleInstance(this: IMParticleWebSDKInstance, instan
128128
integrationDelays: {},
129129
forwarderConstructors: [],
130130
};
131-
this._IntegrationCapture = new IntegrationCapture();
132131
this._RoktManager = new RoktManager();
133132

134133
// required for forwarders once they reference the mparticle instance
@@ -1354,6 +1353,7 @@ export default function mParticleInstance(this: IMParticleWebSDKInstance, instan
13541353
// Some (server) config settings need to be returned before they are set on SDKConfig in a self hosted environment
13551354
function completeSDKInitialization(apiKey, config, mpInstance) {
13561355
const kitBlocker = createKitBlocker(config, mpInstance);
1356+
const { getFeatureFlag } = mpInstance._Helpers;
13571357

13581358
mpInstance._APIClient = new APIClient(mpInstance, kitBlocker);
13591359
mpInstance._Forwarders = new Forwarders(mpInstance, kitBlocker);
@@ -1386,11 +1386,22 @@ function completeSDKInitialization(apiKey, config, mpInstance) {
13861386
? { userIdentities: currentUserIdentities }
13871387
: mpInstance._Store.SDKConfig.identifyRequest;
13881388

1389-
if (mpInstance._Helpers.getFeatureFlag(ReportBatching)) {
1389+
if (getFeatureFlag(ReportBatching)) {
13901390
mpInstance._ForwardingStatsUploader.startForwardingStatsTimer();
13911391
}
1392-
1393-
if (mpInstance._Helpers.getFeatureFlag(CaptureIntegrationSpecificIds)) {
1392+
// https://go.mparticle.com/work/SQDSDKS-7639
1393+
const integrationSpecificIds = getFeatureFlag(CaptureIntegrationSpecificIds) as boolean;
1394+
const integrationSpecificIdsV2 = getFeatureFlag(CaptureIntegrationSpecificIdsV2) as string;
1395+
1396+
const isIntegrationCaptureEnabled = (integrationSpecificIdsV2 && integrationSpecificIdsV2 !== CaptureIntegrationSpecificIdsV2Modes.None) || integrationSpecificIds === true;
1397+
if (isIntegrationCaptureEnabled) {
1398+
let captureMode: valueof<typeof CaptureIntegrationSpecificIdsV2Modes> | undefined;
1399+
if (integrationSpecificIds || integrationSpecificIdsV2 === CaptureIntegrationSpecificIdsV2Modes.All) {
1400+
captureMode = 'all';
1401+
} else if (integrationSpecificIdsV2 === CaptureIntegrationSpecificIdsV2Modes.RoktOnly) {
1402+
captureMode = 'roktonly';
1403+
}
1404+
mpInstance._IntegrationCapture = new IntegrationCapture(captureMode);
13941405
mpInstance._IntegrationCapture.capture();
13951406
}
13961407

src/sdkToEventsApiConverter.ts

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -18,11 +18,10 @@ import { SDKIdentityTypeEnum } from './identity.interfaces';
1818
import Constants from './constants';
1919
import { IMParticleWebSDKInstance } from './mp-instance';
2020

21-
const {
22-
FeatureFlags
23-
} = Constants;
21+
const { FeatureFlags } = Constants;
2422
const {
25-
CaptureIntegrationSpecificIds
23+
CaptureIntegrationSpecificIds,
24+
CaptureIntegrationSpecificIdsV2,
2625
} = FeatureFlags;
2726

2827
type PartnerIdentities = Dictionary<string>;
@@ -127,8 +126,12 @@ export function convertEvents(
127126
},
128127
};
129128
}
129+
// https://go.mparticle.com/work/SQDSDKS-7639
130+
const integrationSpecificIds = getFeatureFlag && Boolean(getFeatureFlag(CaptureIntegrationSpecificIds));
131+
const integrationSpecificIdsV2 = getFeatureFlag && (getFeatureFlag(CaptureIntegrationSpecificIdsV2) as string);
132+
133+
const isIntegrationCaptureEnabled = (integrationSpecificIdsV2 && integrationSpecificIdsV2 !== Constants.CaptureIntegrationSpecificIdsV2Modes.None) || integrationSpecificIds === true;
130134

131-
const isIntegrationCaptureEnabled: boolean = getFeatureFlag && Boolean(getFeatureFlag(CaptureIntegrationSpecificIds));
132135
if (isIntegrationCaptureEnabled) {
133136
const capturedPartnerIdentities: PartnerIdentities = _IntegrationCapture?.getClickIdsAsPartnerIdentities();
134137
if (!isEmpty(capturedPartnerIdentities)) {

src/serverModel.ts

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -276,18 +276,22 @@ export default function ServerModel(
276276
let customFlags: SDKEventCustomFlags = {...event.customFlags};
277277
let integrationAttributes: IntegrationAttributes = mpInstance._Store.integrationAttributes;
278278

279+
const { getFeatureFlag } = mpInstance._Helpers;
279280
// https://go.mparticle.com/work/SQDSDKS-5053
280-
if (mpInstance._Helpers.getFeatureFlag && mpInstance._Helpers.getFeatureFlag(Constants.FeatureFlags.CaptureIntegrationSpecificIds)) {
281-
281+
// https://go.mparticle.com/work/SQDSDKS-7639
282+
const integrationSpecificIds = getFeatureFlag && (getFeatureFlag(Constants.FeatureFlags.CaptureIntegrationSpecificIds) as boolean);
283+
const integrationSpecificIdsV2 = getFeatureFlag && ((getFeatureFlag(Constants.FeatureFlags.CaptureIntegrationSpecificIdsV2) as string) || '');
284+
const isIntegrationCaptureEnabled = (integrationSpecificIdsV2 && integrationSpecificIdsV2 !== Constants.CaptureIntegrationSpecificIdsV2Modes.None) || integrationSpecificIds === true;
285+
if (isIntegrationCaptureEnabled) {
282286
// Attempt to recapture click IDs in case a third party integration
283287
// has added or updated new click IDs since the last event was sent.
284288
mpInstance._IntegrationCapture.capture();
285289
const transformedClickIDs = mpInstance._IntegrationCapture.getClickIdsAsCustomFlags();
286-
customFlags = {...transformedClickIDs, ...customFlags};
287-
290+
customFlags = { ...transformedClickIDs, ...customFlags };
288291
const transformedIntegrationAttributes = mpInstance._IntegrationCapture.getClickIdsAsIntegrationAttributes();
289-
integrationAttributes = {...transformedIntegrationAttributes, ...integrationAttributes};
292+
integrationAttributes = { ...transformedIntegrationAttributes, ...integrationAttributes };
290293
}
294+
291295

292296
if (event.hasOwnProperty('toEventAPIObject')) {
293297
eventObject = event.toEventAPIObject();

src/store.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -145,6 +145,7 @@ export interface IFeatureFlags {
145145
directURLRouting?: boolean;
146146
cacheIdentity?: boolean;
147147
captureIntegrationSpecificIds?: boolean;
148+
captureIntegrationSpecificIdsV2?: string;
148149
astBackgroundEvents?: boolean;
149150
}
150151

@@ -771,6 +772,7 @@ export function processFlags(config: SDKInitConfig): IFeatureFlags {
771772
CacheIdentity,
772773
AudienceAPI,
773774
CaptureIntegrationSpecificIds,
775+
CaptureIntegrationSpecificIdsV2,
774776
AstBackgroundEvents
775777
} = Constants.FeatureFlags;
776778

@@ -790,8 +792,8 @@ export function processFlags(config: SDKInitConfig): IFeatureFlags {
790792
flags[CacheIdentity] = config.flags[CacheIdentity] === 'True';
791793
flags[AudienceAPI] = config.flags[AudienceAPI] === 'True';
792794
flags[CaptureIntegrationSpecificIds] = config.flags[CaptureIntegrationSpecificIds] === 'True';
795+
flags[CaptureIntegrationSpecificIdsV2] = (config.flags[CaptureIntegrationSpecificIdsV2] || 'none');
793796
flags[AstBackgroundEvents] = config.flags[AstBackgroundEvents] === 'True';
794-
795797
return flags;
796798
}
797799

0 commit comments

Comments
 (0)