diff --git a/.github/workflows/integration_test.yml b/.github/workflows/integration_test.yml index 6b73ba748..048ad11c9 100644 --- a/.github/workflows/integration_test.yml +++ b/.github/workflows/integration_test.yml @@ -37,7 +37,7 @@ jobs: SDK: javascript FULLSTACK_TEST_REPO: ${{ inputs.FULLSTACK_TEST_REPO }} BUILD_NUMBER: ${{ github.run_id }} - TESTAPP_BRANCH: master + TESTAPP_BRANCH: test-5.x.x GITHUB_TOKEN: ${{ secrets.CI_USER_TOKEN }} EVENT_TYPE: ${{ github.event_name }} GITHUB_CONTEXT: ${{ toJson(github) }} @@ -53,4 +53,4 @@ jobs: run: | CLIENT=node home/runner/travisci-tools/trigger-script-with-status-update.sh # Run only browser builds when it's FSC not for FPS. - [ "$FULLSTACK_TEST_REPO" == "ProdTesting" ] || CLIENT=browser home/runner/travisci-tools/trigger-script-with-status-update.sh \ No newline at end of file + [ "$FULLSTACK_TEST_REPO" == "ProdTesting" ] || CLIENT=browser home/runner/travisci-tools/trigger-script-with-status-update.sh diff --git a/.github/workflows/javascript.yml b/.github/workflows/javascript.yml index cf3f8c49f..73e3aa048 100644 --- a/.github/workflows/javascript.yml +++ b/.github/workflows/javascript.yml @@ -2,13 +2,13 @@ name: Javascript on: push: - branches: [ master ] + branches: [ 5.x.x ] pull_request: - branches: [ master ] + branches: [ 5.x.x ] jobs: lint_markdown_files: - uses: optimizely/javascript-sdk/.github/workflows/lint_markdown.yml@master + uses: optimizely/javascript-sdk/.github/workflows/lint_markdown.yml@5.x.x lint: runs-on: ubuntu-latest @@ -27,13 +27,13 @@ jobs: npm run lint integration_tests: - uses: optimizely/javascript-sdk/.github/workflows/integration_test.yml@master + uses: optimizely/javascript-sdk/.github/workflows/integration_test.yml@5.x.x secrets: CI_USER_TOKEN: ${{ secrets.CI_USER_TOKEN }} TRAVIS_COM_TOKEN: ${{ secrets.TRAVIS_COM_TOKEN }} fullstack_production_suite: - uses: optimizely/javascript-sdk/.github/workflows/integration_test.yml@master + uses: optimizely/javascript-sdk/.github/workflows/integration_test.yml@5.x.x with: FULLSTACK_TEST_REPO: ProdTesting secrets: diff --git a/CHANGELOG.md b/CHANGELOG.md index a0fd73fdf..abbb80136 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,12 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/) and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html). +## [5.3.5] - Jan 29, 2025 + +### Bug Fixes +- Rollout experiment key exclusion from activate method([#949](https://github.com/optimizely/javascript-sdk/pull/949)) +- Using `optimizely.readyPromise` instead of `optimizely.onReady` to avoid setTimeout call in edge environments. ([#995](https://github.com/optimizely/javascript-sdk/pull/995)) + ## [5.3.4] - Jun 28, 2024 ### Changed diff --git a/lib/core/decision_service/index.tests.js b/lib/core/decision_service/index.tests.js index ba0dd5fbb..f61d67630 100644 --- a/lib/core/decision_service/index.tests.js +++ b/lib/core/decision_service/index.tests.js @@ -1675,6 +1675,7 @@ describe('lib/core/decision_service', function() { status: 'Not started', key: '594031', id: '594031', + isRollout: true, variations: [ { id: '594032', @@ -1812,6 +1813,7 @@ describe('lib/core/decision_service', function() { status: 'Not started', key: '594037', id: '594037', + isRollout: true, variations: [ { id: '594038', @@ -1996,6 +1998,7 @@ describe('lib/core/decision_service', function() { status: 'Not started', key: '594037', id: '594037', + isRollout: true, variations: [ { id: '594038', @@ -2150,6 +2153,7 @@ describe('lib/core/decision_service', function() { layerId: '599055', forcedVariations: {}, audienceIds: [], + isRollout: true, variations: [ { key: '599057', diff --git a/lib/core/event_builder/build_event_v1.ts b/lib/core/event_builder/build_event_v1.ts index b1f5b271d..a6506792b 100644 --- a/lib/core/event_builder/build_event_v1.ts +++ b/lib/core/event_builder/build_event_v1.ts @@ -13,13 +13,9 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -import { - EventTags, - ConversionEvent, - ImpressionEvent, -} from '../../modules/event_processor'; +import { ConversionEvent, ImpressionEvent } from '../../modules/event_processor'; -import { Event } from '../../shared_types'; +import { Event, EventTags } from '../../shared_types'; type ProcessableEvent = ConversionEvent | ImpressionEvent diff --git a/lib/core/project_config/index.ts b/lib/core/project_config/index.ts index 68ffbeacd..a52b46efa 100644 --- a/lib/core/project_config/index.ts +++ b/lib/core/project_config/index.ts @@ -167,6 +167,7 @@ export const createProjectConfig = function(datafileObj?: JSON, datafileStr: str projectConfig.rolloutIdMap = keyBy(projectConfig.rollouts || [], 'id'); objectValues(projectConfig.rolloutIdMap || {}).forEach(rollout => { (rollout.experiments || []).forEach(experiment => { + experiment.isRollout = true projectConfig.experiments.push(experiment); // Creates { : } map inside of the experiment experiment.variationKeyMap = keyBy(experiment.variations, 'key'); diff --git a/lib/index.browser.tests.js b/lib/index.browser.tests.js index e14b91463..a01e60ef0 100644 --- a/lib/index.browser.tests.js +++ b/lib/index.browser.tests.js @@ -193,7 +193,7 @@ describe('javascript-sdk (Browser)', function() { optlyInstance.onReady().catch(function() {}); assert.instanceOf(optlyInstance, Optimizely); - assert.equal(optlyInstance.clientVersion, '5.3.4'); + assert.equal(optlyInstance.clientVersion, '5.3.5'); }); it('should set the JavaScript client engine and version', function() { @@ -584,6 +584,10 @@ describe('javascript-sdk (Browser)', function() { const fakeOptimizely = { onReady: () => Promise.resolve({ success: true }), identifyUser: sinon.stub().returns(), + isOdpIntegrated: () => true, + readyPromise: Promise.resolve({ + success: true, + }), }; const fakeErrorHandler = { handleError: function() {} }; diff --git a/lib/index.lite.tests.js b/lib/index.lite.tests.js index 30282dcf5..5eaaf80cc 100644 --- a/lib/index.lite.tests.js +++ b/lib/index.lite.tests.js @@ -76,7 +76,7 @@ describe('optimizelyFactory', function() { optlyInstance.onReady().catch(function() {}); assert.instanceOf(optlyInstance, Optimizely); - assert.equal(optlyInstance.clientVersion, '5.3.4'); + assert.equal(optlyInstance.clientVersion, '5.3.5'); }); }); }); diff --git a/lib/index.node.tests.js b/lib/index.node.tests.js index 98aac4c97..f66dc0b69 100644 --- a/lib/index.node.tests.js +++ b/lib/index.node.tests.js @@ -90,7 +90,7 @@ describe('optimizelyFactory', function() { optlyInstance.onReady().catch(function() {}); assert.instanceOf(optlyInstance, Optimizely); - assert.equal(optlyInstance.clientVersion, '5.3.4'); + assert.equal(optlyInstance.clientVersion, '5.3.5'); }); describe('event processor configuration', function() { diff --git a/lib/modules/event_processor/events.ts b/lib/modules/event_processor/events.ts index 65cce503b..61abab4e5 100644 --- a/lib/modules/event_processor/events.ts +++ b/lib/modules/event_processor/events.ts @@ -1,3 +1,5 @@ +import { EventTags } from "../../shared_types" + /** * Copyright 2022, Optimizely * @@ -82,10 +84,6 @@ export interface ConversionEvent extends BaseEvent { tags: EventTags | undefined } -export type EventTags = { - [key: string]: string | number | null -} - export function areEventContextsEqual(eventA: BaseEvent, eventB: BaseEvent): boolean { const contextA = eventA.context const contextB = eventB.context diff --git a/lib/modules/event_processor/v1/buildEventV1.ts b/lib/modules/event_processor/v1/buildEventV1.ts index 699498dc4..c0ab1aba7 100644 --- a/lib/modules/event_processor/v1/buildEventV1.ts +++ b/lib/modules/event_processor/v1/buildEventV1.ts @@ -13,9 +13,10 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -import { EventTags, ConversionEvent, ImpressionEvent, VisitorAttribute } from '../events' +import { ConversionEvent, ImpressionEvent, VisitorAttribute } from '../events' import { ProcessableEvent } from '../eventProcessor' import { EventV1Request } from '../eventDispatcher' +import { EventTags } from '../../../shared_types' const ACTIVATE_EVENT_KEY = 'campaign_activated' const CUSTOM_ATTRIBUTE_FEATURE_TYPE = 'custom' diff --git a/lib/optimizely/index.ts b/lib/optimizely/index.ts index 2a3eb5a0d..8605adec0 100644 --- a/lib/optimizely/index.ts +++ b/lib/optimizely/index.ts @@ -479,7 +479,7 @@ export default class Optimizely implements Client { } const experiment = configObj.experimentKeyMap[experimentKey]; - if (!experiment) { + if (!experiment || experiment.isRollout) { this.logger.log(LOG_LEVEL.DEBUG, ERROR_MESSAGES.INVALID_EXPERIMENT_KEY, MODULE_NAME, experimentKey); return null; } diff --git a/lib/optimizely_user_context/index.ts b/lib/optimizely_user_context/index.ts index 0b689237a..b9939ae51 100644 --- a/lib/optimizely_user_context/index.ts +++ b/lib/optimizely_user_context/index.ts @@ -64,8 +64,10 @@ export default class OptimizelyUserContext implements IOptimizelyUserContext { this.forcedDecisionsMap = {}; if (shouldIdentifyUser) { - this.optimizely.onReady().then(({ success }) => { - if (success) { + // eslint-disable-next-line @typescript-eslint/ban-ts-comment + // @ts-ignore + optimizely.readyPromise.then(({ success }) => { + if (success && optimizely.isOdpIntegrated()) { this.identifyUser(); } }); diff --git a/lib/shared_types.ts b/lib/shared_types.ts index 08291ecf2..75885b83e 100644 --- a/lib/shared_types.ts +++ b/lib/shared_types.ts @@ -74,7 +74,10 @@ export interface UserProfile { } export type EventTags = { - [key: string]: string | number | null; + revenue?: string | number | null; + value?: string | number | null; + $opt_event_properties?: Record; + [key: string]: unknown; }; export interface UserProfileService { @@ -178,6 +181,7 @@ export interface Experiment { audienceIds: string[]; trafficAllocation: TrafficAllocation[]; forcedVariations?: { [key: string]: string }; + isRollout?: boolean; } export enum VariableType { diff --git a/lib/tests/test_data.js b/lib/tests/test_data.js index eddf1a3fd..eb38fa6cf 100644 --- a/lib/tests/test_data.js +++ b/lib/tests/test_data.js @@ -1647,6 +1647,7 @@ export var datafileWithFeaturesExpectedData = { status: 'Not started', key: '599056', id: '599056', + isRollout: true, variationKeyMap: { 599057: { key: '599057', @@ -1711,6 +1712,7 @@ export var datafileWithFeaturesExpectedData = { ], key: '594031', id: '594031', + isRollout: true, variationKeyMap: { 594032: { variables: [ @@ -1783,6 +1785,7 @@ export var datafileWithFeaturesExpectedData = { ], key: '594037', id: '594037', + isRollout: true, variationKeyMap: { 594038: { variables: [ @@ -1856,6 +1859,7 @@ export var datafileWithFeaturesExpectedData = { ], key: '594060', id: '594060', + isRollout: true, variationKeyMap: { 594061: { variables: [ @@ -1920,6 +1924,7 @@ export var datafileWithFeaturesExpectedData = { ], key: '594066', id: '594066', + isRollout: true, variationKeyMap: { 594067: { variables: [ diff --git a/lib/utils/enums/index.ts b/lib/utils/enums/index.ts index 962d06c30..404bca534 100644 --- a/lib/utils/enums/index.ts +++ b/lib/utils/enums/index.ts @@ -221,7 +221,7 @@ export const NODE_CLIENT_ENGINE = 'node-sdk'; export const REACT_CLIENT_ENGINE = 'react-sdk'; export const REACT_NATIVE_CLIENT_ENGINE = 'react-native-sdk'; export const REACT_NATIVE_JS_CLIENT_ENGINE = 'react-native-js-sdk'; -export const CLIENT_VERSION ='5.3.4' +export const CLIENT_VERSION ='5.3.5' export const DECISION_NOTIFICATION_TYPES = { AB_TEST: 'ab-test', diff --git a/lib/utils/event_tag_utils/index.ts b/lib/utils/event_tag_utils/index.ts index 7917f3baa..e3550a73d 100644 --- a/lib/utils/event_tag_utils/index.ts +++ b/lib/utils/event_tag_utils/index.ts @@ -13,9 +13,8 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -import { EventTags } from '../../modules/event_processor'; +import { EventTags } from '../../shared_types'; import { LoggerFacade } from '../../modules/logging'; - import { LOG_LEVEL, LOG_MESSAGES, @@ -42,7 +41,7 @@ export function getRevenueValue(eventTags: EventTags, logger: LoggerFacade): num return null; } - const parsedRevenueValue = typeof rawValue === 'string' ? parseInt(rawValue) : rawValue; + const parsedRevenueValue = typeof rawValue === 'string' ? parseInt(rawValue) : Math.trunc(rawValue); if (isFinite(parsedRevenueValue)) { logger.log(LOG_LEVEL.INFO, LOG_MESSAGES.PARSED_REVENUE_VALUE, MODULE_NAME, parsedRevenueValue); @@ -66,7 +65,7 @@ export function getEventValue(eventTags: EventTags, logger: LoggerFacade): numbe return null; } - const parsedEventValue = typeof rawValue === 'string' ? parseFloat(rawValue) : rawValue; + const parsedEventValue = typeof rawValue === 'string' ? parseFloat(rawValue): rawValue; if (isFinite(parsedEventValue)) { logger.log(LOG_LEVEL.INFO, LOG_MESSAGES.PARSED_NUMERIC_VALUE, MODULE_NAME, parsedEventValue); @@ -75,4 +74,4 @@ export function getEventValue(eventTags: EventTags, logger: LoggerFacade): numbe logger.log(LOG_LEVEL.INFO, LOG_MESSAGES.FAILED_TO_PARSE_VALUE, MODULE_NAME, rawValue); return null; } -} \ No newline at end of file +} diff --git a/package-lock.json b/package-lock.json index 28b366dc1..35bbbae1a 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@optimizely/optimizely-sdk", - "version": "5.3.4", + "version": "5.3.5", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "@optimizely/optimizely-sdk", - "version": "5.3.4", + "version": "5.3.5", "license": "Apache-2.0", "dependencies": { "decompress-response": "^4.2.1", diff --git a/package.json b/package.json index 75d8fe42e..1b93e7539 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@optimizely/optimizely-sdk", - "version": "5.3.4", + "version": "5.3.5", "description": "JavaScript SDK for Optimizely Feature Experimentation, Optimizely Full Stack (legacy), and Optimizely Rollouts", "module": "dist/optimizely.browser.es.js", "main": "dist/optimizely.node.min.js", diff --git a/tests/index.react_native.spec.ts b/tests/index.react_native.spec.ts index a11f40c32..11bed1752 100644 --- a/tests/index.react_native.spec.ts +++ b/tests/index.react_native.spec.ts @@ -93,7 +93,7 @@ describe('javascript-sdk/react-native', () => { expect(optlyInstance).toBeInstanceOf(Optimizely); // @ts-ignore - expect(optlyInstance.clientVersion).toEqual('5.3.4'); + expect(optlyInstance.clientVersion).toEqual('5.3.5'); }); it('should set the React Native JS client engine and javascript SDK version', () => {