Fix/optimizely personalization campaign#1463
Conversation
…campaign getActiveExperimentIds() does not surface campaignId (layerId) for Optimizely Personalization campaigns. Switching to getCampaignStates() correctly captures the campaign layer for both Web Experimentation and Personalization campaigns. Also adds campaignId to OptimizelyxSummary interface and bumps schema reference to 1-1-0.
|
Thanks for your pull request. Is this your first contribution to a Snowplow open source project? Before we can look at your pull request, you'll need to sign a Contributor License Agreement (CLA). 📝 Please visit https://docs.snowplowanalytics.com/docs/contributing/contributor-license-agreement/ to learn more and sign. Once you've signed, please reply here (e.g. I signed it!) and we'll verify. Thanks. |
|
I Signed It!
James Shebester
…________________________________
From: Snowplow CLA bot ***@***.***>
Sent: Wednesday, March 25, 2026 8:34 PM
To: snowplow/snowplow-javascript-tracker ***@***.***>
Cc: James Shebester ***@***.***>; Author ***@***.***>
Subject: Re: [snowplow/snowplow-javascript-tracker] Fix/optimizely personalization campaign (PR #1463)
snowplowcla left a comment (snowplow/snowplow-javascript-tracker#1463) Thanks for your pull request. Is this your first contribution to a Snowplow open source project? Before we can look at your pull request, you'll need to sign a Contributor License Agreement (CLA). 📝 Please vi
[https://image-processing-service.eu-2.mimecastcybergraph.com/v2/banners?e=fzwHFKB9H7aN5DE-XL1rMPrmKVhG5HjxpvVWbiUWzgcGlz-KFZiWmr9zKD6gyRle8NrQb4I8Tz8n6mPDm_zoGkLo93cu7HC0aY39qYAy1RlYjaTWThH4xN7sk4XquEMH5FXedHf5zGO6qJzXQmXcQRhTQ5aBGzWsmXYNBJS-8zeCJr1k5uEXN77m4IvAv0PBbtTpacyUcXWLNGJBtrPG_R5NFSG0sS_v0Jr7jpdtRiCmUKbZ7uq6WgLE4dYLh7j7HOL_ccV4heeHb8-aNH_DZhLUla3id302_HMT3GN9R_DK77Eysh2oDgk5NWJaeStpJa0mZYoQiXn1rbN8h9TsyzCHHOUmKQIsku6i46pX_z6A52aPTsV4xl0AD-xJnnhpC8aR6_5yXaVP-HwnDQO87iBBHJyiyPVTpB2y5EbrYClSJmsX-9Zoo_lckm11I-Y=]<https://report.mimecastcybergraph.com?magiclink=https%3A%2F%2Fapi.services.mimecast.com%2Foauth2%2Fauthorize%3Fresponse_type%3Dcode%26client_id%3Do20nRkVXf7VUVnANkXhoOwGytEwGN0YAlyeDJn7oBTGNl2kN%26state%3DeyJhbGciOiJSU0EtT0FFUC0yNTYiLCJlbmMiOiJBMjU2R0NNIn0.KdyVL6FjujlpoS9C5bzl1beh8xTV_qV-lRCx33Ch51OM4wOI4pH68iHhKAtuEifiz-Q0v25HMOe5Smw-RMeINAu87wmK2AtG0qGElxFlPylMvAa2xz1DMomosvvNEbAQxTl2zXEOL1lpeo6S3sXhSfmFxqKOeaDNCNtHW1zJ8FCyIHZVFm66bzKQJQHOi3qp0YMd8ZcuuHpabs73TVgMCgrPh6T96BuGpfnbfWebWmjrY6dsHciFkP7o8Zi_pbjiDvs8nZtgk94RaZLeL-QgkdSwzujLU6DASlrwd8rXtDYWZl8kw95tWOG644OPv4ZQ3S4UdXn8FF1ADiEozCqZiA._3IFqXev0rAttRil._pdkFZKO7EpY-LuuNpyEctxHNw2w24EAc_ZzzKLrhEC59g300k_1UzptsV7fwWCEGt0MODfKLYDNjdhpficsYRRGO1ZMDWES9idoC23b973crKJXEPsk74Ex3VLG5lq3n4vukjsrN11EpkEuX-YUpxtT-AXdOl3wcIotHiRTfa5CfvpCSkkUgnshqI9py5gE_k_BQVAYAWLpFlvvq8Qn0QkTdKSg3foqlAE3R2IecHM-ZIckXSqUykX_Q4mv0PLGgScDFFZ0gJsLxiD1N34ubyX_H8xO4GekRgSgHfGUGZEBeesEhDUVK0nF4j5dLKXVVQ5219CvcMFF1dAukZnuH33725ew25FZiztF1Bm_8fySyiVorMAMaZV9H3mvlT_y2lCTkIpr_FjNqzQf4HDC3e6GegvVfJL8JiWGTVxWINijW9S_wjmIJUX5YKs1YGgJSoke1K-lWeocQpDwwNVKcLlRwuTHlm3MyABkZnmJnJmerdr7G7uxWqbOAsU34n1kmXtcvPWCT-qd3WKFvyfsTBnWxRXXdV9IeWY5_d5j90bf_OWVlyMW7Nm4iwsjc0QuknoPLtxSgV6WdBU0z7n0A23HSDbdxWDpnR_ivghrkSx4gZL1xyQHNtxOfQJh2yFIXMTLgfQQVp6yEVe3y2mXYnLxKUxvodZFufatBduZxfA4sm3L8fZyoXjLC6dK66zEIB-zJVAup5k-wgpvWpapFddnoINgkQgWz0YwuBZH8TxpulBHNgx1QcIIJIlxaUeG205jrbxWZDVdPdy4KPPhsx9zZT9n19HPWQsdOQxb1mCZJtCNpkbnXUhHOgU1k99XIoINFLVlAodXgIFpWhmDLZ813Q67OsWpk0xKygLpyF32PQ19CrfX4QNCZm1v8dgLCTq6CBkifQ.PXLnHvnH0iLLCpY8iHCHfQ%26redirect_uri%3Dhttps%3A%2F%2Freport.mimecastcybergraph.com%2Fcallback>
CGBANNERINDICATOR
[https://image-tracking-service.eu-2.mimecastcybergraph.com/v1/image?imageData=663JbMyMFkyQ%2FvYSTFqCFIaXG59S0E3TVU79u0xkHqlWswVpIV40H3PhM6s2JalDSF2SzTyS232gma%2BbwACBdptBm3iaxS%2BQl%2BfPPsqmj0rVmITe1eTVvHKa9vQo4n4PhMo8hGx%2FVbHFH%2FTQ9oDIkDO8ctp5Duw7jldIexooDMpjTn3P0G9FJCoTFEkF6GTlUQIL0gdubG0cB2VvCnV6TiBDdE39IovTwTAILyBdd0G5sekSaLNotJETFgodw6UZbo6zCqUHybV7B9DavMbrPzJJkjJBNGo3OQfIGn7WELsi2I3CFdLR]snowplowcla left a comment (snowplow/snowplow-javascript-tracker#1463)<#1463?email_source=notifications&email_token=AXSE25LAXSFGC6AGCAQTQH34SSCJNA5CNFSNUABFM5UWIORPF5TWS5BNNB2WEL2JONZXKZKDN5WW2ZLOOQXTIMJTGA4TENZWGA4KM4TFMFZW63VGMF2XI2DPOKSWK5TFNZ2LK4DSL5RW63LNMVXHIX3POBSW4X3DNRUWG2Y#issuecomment-4130927608>
Thanks for your pull request. Is this your first contribution to a Snowplow open source project? Before we can look at your pull request, you'll need to sign a Contributor License Agreement (CLA).
📝 Please visit https://docs.snowplowanalytics.com/docs/contributing/contributor-license-agreement/<https://docs.snowplowanalytics.com/docs/contributing/contributor-license-agreement/> to learn more and sign.
Once you've signed, please reply here (e.g. I signed it!) and we'll verify. Thanks.
—
Reply to this email directly, view it on GitHub<#1463?email_source=notifications&email_token=AXSE25LAXSFGC6AGCAQTQH34SSCJNA5CNFSNUABFM5UWIORPF5TWS5BNNB2WEL2JONZXKZKDN5WW2ZLOOQXTIMJTGA4TENZWGA4KM4TFMFZW63VGMF2XI2DPOKSWK5TFNZ2LK4DSL5RW63LNMVXHIX3POBSW4X3DNRUWG2Y#issuecomment-4130927608>, or unsubscribe<https://github.com/notifications/unsubscribe-auth/AXSE25MSOUMATTHSOYAEPXL4SSCJNAVCNFSM6AAAAACW7VWYYCVHI2DSMVQWIX3LMV43OSLTON2WKQ3PNVWWK3TUHM2DCMZQHEZDONRQHA>.
You are receiving this because you authored the thread.Message ID: ***@***.***>
|
|
Confirmed! James Shebester (@JamesShebester) has signed the Contributor License Agreement. Thanks so much. |
|
Thanks for this contribution James Shebester (@JamesShebester)! We'll take a look and get the Iglu Central PR raised on our side for the schema version bump. |
There was a problem hiding this comment.
Pull request overview
Updates the Optimizely X browser plugin to correctly include campaignId (layerId) in the emitted Optimizely summary context, especially for Optimizely Personalization campaigns where getActiveExperimentIds() does not expose the campaign layer.
Changes:
- Switch summary extraction to
state.getCampaignStates({ isActive: true })and includecampaignIdin the mapped context data. - Extend the
OptimizelyxSummaryTypeScript interface to includecampaignId. - Bump the emitted Iglu schema reference from
1-0-0to1-1-0.
Reviewed changes
Copilot reviewed 2 out of 3 changed files in this pull request and generated 4 comments.
| File | Description |
|---|---|
| plugins/browser-plugin-optimizely-x/src/index.ts | Uses getCampaignStates to map active campaigns and emits schema 1-1-0. |
| plugins/browser-plugin-optimizely-x/src/contexts.ts | Adds campaignId to the summary context interface. |
| plugins/browser-plugin-optimizely-x/pnpm-lock.yaml | Introduces a per-package lockfile (conflicts with Rush-managed lockfile conventions). |
Files not reviewed (1)
- plugins/browser-plugin-optimizely-x/pnpm-lock.yaml: Language not supported
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| dependencies: | ||
| '@snowplow/browser-tracker-core': | ||
| specifier: ~4.6.9 | ||
| version: 4.6.9 | ||
| '@snowplow/tracker-core': | ||
| specifier: ~4.6.9 | ||
| version: 4.6.9 |
There was a problem hiding this comment.
The dependency entries in this lockfile pin @snowplow/* to ~4.6.9, which conflicts with this package's package.json (workspace:* deps and peer @snowplow/browser-tracker ~4.7.0). If this lockfile is kept, it should be regenerated from the monorepo install so versions/specifiers match the workspace configuration; otherwise remove it.
| * Schema for an Optimizely X summary context | ||
| */ | ||
| export interface OptimizelyxSummary { | ||
| campaignId?: number | null; // added: captures the Personalization campaign layerId |
There was a problem hiding this comment.
This inline comment implies campaignId is only for Personalization, but index.ts now populates campaignId for all campaign types (Web Experimentation + Personalization). Update the comment to avoid misleading future readers.
| campaignId?: number | null; // added: captures the Personalization campaign layerId | |
| campaignId?: number | null; // captures the campaign/layer identifier for Web Experimentation and Personalization |
| return getOptimizelyXSummary().map(function (experiment) { | ||
| return { | ||
| schema: 'iglu:com.optimizely.optimizelyx/summary/jsonschema/1-0-0', | ||
| schema: 'iglu:com.optimizely.optimizelyx/summary/jsonschema/1-1-0', | ||
| data: experiment, |
There was a problem hiding this comment.
The callback parameter name experiment is now a bit misleading since each element can represent a campaign summary (it includes campaignId). Renaming it to something like summary/contextData would improve readability and avoid confusion.
| lockfileVersion: '9.0' | ||
|
|
||
| settings: | ||
| autoInstallPeers: true | ||
| excludeLinksFromLockfile: false | ||
|
|
||
| importers: | ||
|
|
||
| .: | ||
| dependencies: | ||
| '@snowplow/browser-tracker-core': | ||
| specifier: ~4.6.9 | ||
| version: 4.6.9 | ||
| '@snowplow/tracker-core': | ||
| specifier: ~4.6.9 | ||
| version: 4.6.9 | ||
|
|
||
| packages: | ||
|
|
||
| '@snowplow/browser-tracker-core@4.6.9': | ||
| resolution: {integrity: sha512-KyW/zU7Isb49/2XcliwTb7yOZvowHXBfdlODiEOGXWn9QcWyAEawqETh/gZJua/+lg+9d6zp2F9bqkKPpwtpKg==} | ||
|
|
||
| '@snowplow/tracker-core@4.6.9': | ||
| resolution: {integrity: sha512-ybKK3t7pd+JwX9BA+DeiuyzbOgE0RpbzhTpPwHvIom+gAQUYCZpyECxFT5FBLrdZtGeWdIaoY93JXneDNfVGLg==} | ||
|
|
||
| charenc@0.0.2: | ||
| resolution: {integrity: sha512-yrLQ/yVUFXkzg7EDQsPieE/53+0RlaWTs+wBrvW36cyilJ2SaDWfl4Yj7MtLTXleV9uEKefbAGUPv2/iWSooRA==} | ||
|
|
||
| crypt@0.0.2: | ||
| resolution: {integrity: sha512-mCxBlsHFYh9C+HVpiEacem8FEBnMXgU9gy4zmNC+SXAZNB/1idgp/aulFJ4FgCi7GPEVbfyng092GqL2k2rmow==} | ||
|
|
||
| sha1@1.1.1: | ||
| resolution: {integrity: sha512-dZBS6OrMjtgVkopB1Gmo4RQCDKiZsqcpAQpkV/aaj+FCrCg8r4I4qMkDPQjBgLIxlmu9k4nUbWq6ohXahOneYA==} | ||
|
|
||
| tslib@2.8.1: | ||
| resolution: {integrity: sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==} | ||
|
|
||
| uuid@10.0.0: | ||
| resolution: {integrity: sha512-8XkAphELsDnEGrDxUOHB3RGvXz6TeuYSGEZBOjtTtPm2lwhGBjLgOzLHB63IUWfBpNucQjND6d3AOudO+H3RWQ==} | ||
| hasBin: true | ||
|
|
||
| snapshots: | ||
|
|
||
| '@snowplow/browser-tracker-core@4.6.9': | ||
| dependencies: | ||
| '@snowplow/tracker-core': 4.6.9 | ||
| sha1: 1.1.1 | ||
| tslib: 2.8.1 | ||
| uuid: 10.0.0 | ||
|
|
||
| '@snowplow/tracker-core@4.6.9': | ||
| dependencies: | ||
| tslib: 2.8.1 | ||
| uuid: 10.0.0 | ||
|
|
||
| charenc@0.0.2: {} | ||
|
|
||
| crypt@0.0.2: {} | ||
|
|
||
| sha1@1.1.1: | ||
| dependencies: | ||
| charenc: 0.0.2 | ||
| crypt: 0.0.2 | ||
|
|
||
| tslib@2.8.1: {} | ||
|
|
||
| uuid@10.0.0: {} |
There was a problem hiding this comment.
This repo uses Rush with a single pnpm lockfile under common/config/rush/pnpm-lock.yaml (see .github workflows hashing that file). Committing an additional per-package pnpm-lock.yaml here is likely unused by CI and can cause confusing, divergent dependency installs if someone runs pnpm in this folder. Consider removing this file and relying on the Rush-managed lockfile instead.
| lockfileVersion: '9.0' | |
| settings: | |
| autoInstallPeers: true | |
| excludeLinksFromLockfile: false | |
| importers: | |
| .: | |
| dependencies: | |
| '@snowplow/browser-tracker-core': | |
| specifier: ~4.6.9 | |
| version: 4.6.9 | |
| '@snowplow/tracker-core': | |
| specifier: ~4.6.9 | |
| version: 4.6.9 | |
| packages: | |
| '@snowplow/browser-tracker-core@4.6.9': | |
| resolution: {integrity: sha512-KyW/zU7Isb49/2XcliwTb7yOZvowHXBfdlODiEOGXWn9QcWyAEawqETh/gZJua/+lg+9d6zp2F9bqkKPpwtpKg==} | |
| '@snowplow/tracker-core@4.6.9': | |
| resolution: {integrity: sha512-ybKK3t7pd+JwX9BA+DeiuyzbOgE0RpbzhTpPwHvIom+gAQUYCZpyECxFT5FBLrdZtGeWdIaoY93JXneDNfVGLg==} | |
| charenc@0.0.2: | |
| resolution: {integrity: sha512-yrLQ/yVUFXkzg7EDQsPieE/53+0RlaWTs+wBrvW36cyilJ2SaDWfl4Yj7MtLTXleV9uEKefbAGUPv2/iWSooRA==} | |
| crypt@0.0.2: | |
| resolution: {integrity: sha512-mCxBlsHFYh9C+HVpiEacem8FEBnMXgU9gy4zmNC+SXAZNB/1idgp/aulFJ4FgCi7GPEVbfyng092GqL2k2rmow==} | |
| sha1@1.1.1: | |
| resolution: {integrity: sha512-dZBS6OrMjtgVkopB1Gmo4RQCDKiZsqcpAQpkV/aaj+FCrCg8r4I4qMkDPQjBgLIxlmu9k4nUbWq6ohXahOneYA==} | |
| tslib@2.8.1: | |
| resolution: {integrity: sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==} | |
| uuid@10.0.0: | |
| resolution: {integrity: sha512-8XkAphELsDnEGrDxUOHB3RGvXz6TeuYSGEZBOjtTtPm2lwhGBjLgOzLHB63IUWfBpNucQjND6d3AOudO+H3RWQ==} | |
| hasBin: true | |
| snapshots: | |
| '@snowplow/browser-tracker-core@4.6.9': | |
| dependencies: | |
| '@snowplow/tracker-core': 4.6.9 | |
| sha1: 1.1.1 | |
| tslib: 2.8.1 | |
| uuid: 10.0.0 | |
| '@snowplow/tracker-core@4.6.9': | |
| dependencies: | |
| tslib: 2.8.1 | |
| uuid: 10.0.0 | |
| charenc@0.0.2: {} | |
| crypt@0.0.2: {} | |
| sha1@1.1.1: | |
| dependencies: | |
| charenc: 0.0.2 | |
| crypt: 0.0.2 | |
| tslib@2.8.1: {} | |
| uuid@10.0.0: {} |
Matus Tomlein (matus-tomlein)
left a comment
There was a problem hiding this comment.
Thank you for the contribution James Shebester (@JamesShebester)! We will work on getting the schema published and finalizing this into a tracker release on our side.
653f446
into
snowplow:fix/optimizely-personalization-campaign-id
Summary
Fixes #1462
Replaces
getActiveExperimentIds()withgetCampaignStates({ isActive: true })ingetOptimizelyXSummary()to correctly capturecampaignIdfor Optimizely Personalization campaigns.Problem
The existing implementation uses two Optimizely state API calls that
bypass the campaign layer entirely:
For Optimizely Personalization campaigns,
getActiveExperimentIds()surfaces experience IDs (the sub-unit inside a campaign) but never
exposes the parent
campaignId(layerId). This meanscampaignIdwas never captured in Snowplow event data — not as null, not as empty — it simply did not exist in the schema output.
Solution
getCampaignStates({ isActive: true })returns a unified state objectfor both Web Experimentation and Personalization campaigns, where the
top-level key is the
campaignId:Behavior by Campaign Type
campaignIdexperimentIdvariationChanges
src/index.ts— replacedgetActiveExperimentIds()+getVariationMap()with
getCampaignStates({ isActive: true }), movedvisitorIdresolutionoutside the loop, updated schema reference to
1-1-0src/contexts.ts— addedcampaignId?: number | nulltoOptimizelyxSummaryinterfaceSchema
This change requires a new schema version
iglu:com.optimizely.optimizelyx/summary/jsonschema/1-1-0withcampaignIdadded as an optional integer field. This is a minor version bump
(
1-0-0→1-1-0) as the addition is backward compatible — existing events withoutcampaignIdremain valid.Testing
Verified in browser console against a live Optimizely Personalization
campaign: