diff --git a/packages/optimizely-sdk/lib/core/decision_service/index.ts b/packages/optimizely-sdk/lib/core/decision_service/index.ts index c7e5e9cb7..3ec800593 100644 --- a/packages/optimizely-sdk/lib/core/decision_service/index.ts +++ b/packages/optimizely-sdk/lib/core/decision_service/index.ts @@ -1,5 +1,5 @@ /**************************************************************************** - * Copyright 2017-2021 Optimizely, Inc. and contributors * + * Copyright 2017-2022 Optimizely, Inc. and contributors * * * * Licensed under the Apache License, Version 2.0 (the "License"); * * you may not use this file except in compliance with the License. * @@ -776,6 +776,98 @@ export class DecisionService { return bucketingId; } + /** + * Finds a validated forced decision for specific flagKey and optional ruleKey. + * @param {ProjectConfig} config A projectConfig. + * @param {OptimizelyUserContext} user A Optimizely User Context. + * @param {string} flagKey A flagKey. + * @param {ruleKey} ruleKey A ruleKey (optional). + * @return {DecisionResponse} DecisionResponse object containing valid variation object and decide reasons. + */ + findValidatedForcedDecision( + config: ProjectConfig, + user: OptimizelyUserContext, + flagKey: string, + ruleKey?: string + ): DecisionResponse { + + const decideReasons: (string | number)[][] = []; + const forcedDecision = user.getForcedDecision({ flagKey, ruleKey }); + let variation = null; + let variationKey; + const userId = user.getUserId() + if (config && forcedDecision) { + variationKey = forcedDecision.variationKey; + variation = getFlagVariationByKey(config, flagKey, variationKey); + if (variation) { + if (ruleKey) { + this.logger.log( + LOG_LEVEL.INFO, + LOG_MESSAGES.USER_HAS_FORCED_DECISION_WITH_RULE_SPECIFIED, + variationKey, + flagKey, + ruleKey, + userId + ); + decideReasons.push([ + LOG_MESSAGES.USER_HAS_FORCED_DECISION_WITH_RULE_SPECIFIED, + variationKey, + flagKey, + ruleKey, + userId + ]); + } else { + this.logger.log( + LOG_LEVEL.INFO, + LOG_MESSAGES.USER_HAS_FORCED_DECISION_WITH_NO_RULE_SPECIFIED, + variationKey, + flagKey, + userId + ); + decideReasons.push([ + LOG_MESSAGES.USER_HAS_FORCED_DECISION_WITH_NO_RULE_SPECIFIED, + variationKey, + flagKey, + userId + ]) + } + } else { + if (ruleKey) { + this.logger.log( + LOG_LEVEL.INFO, + LOG_MESSAGES.USER_HAS_FORCED_DECISION_WITH_RULE_SPECIFIED_BUT_INVALID, + flagKey, + ruleKey, + userId + ); + decideReasons.push([ + LOG_MESSAGES.USER_HAS_FORCED_DECISION_WITH_RULE_SPECIFIED_BUT_INVALID, + flagKey, + ruleKey, + userId + ]); + } else { + this.logger.log( + LOG_LEVEL.INFO, + LOG_MESSAGES.USER_HAS_FORCED_DECISION_WITH_NO_RULE_SPECIFIED_BUT_INVALID, + flagKey, + userId + ); + decideReasons.push([ + LOG_MESSAGES.USER_HAS_FORCED_DECISION_WITH_NO_RULE_SPECIFIED_BUT_INVALID, + flagKey, + userId + ]) + } + } + } + + return { + result: variation, + reasons: decideReasons, + } + } + /** * Removes forced variation for given userId and experimentKey * @param {string} userId String representing the user id @@ -1021,7 +1113,7 @@ export class DecisionService { const decideReasons: (string | number)[][] = []; // check forced decision first - const forcedDecisionResponse = user.findValidatedForcedDecision(configObj, flagKey, rule.key); + const forcedDecisionResponse = this.findValidatedForcedDecision(configObj, user, flagKey, rule.key); decideReasons.push(...forcedDecisionResponse.reasons); const forcedVariaton = forcedDecisionResponse.result; @@ -1053,7 +1145,7 @@ export class DecisionService { // check forced decision first const rule = rules[ruleIndex]; - const forcedDecisionResponse = user.findValidatedForcedDecision(configObj, flagKey, rule.key); + const forcedDecisionResponse = this.findValidatedForcedDecision(configObj, user, flagKey, rule.key); decideReasons.push(...forcedDecisionResponse.reasons); const forcedVariaton = forcedDecisionResponse.result; diff --git a/packages/optimizely-sdk/lib/optimizely/index.ts b/packages/optimizely-sdk/lib/optimizely/index.ts index f52388f3a..58bc0e712 100644 --- a/packages/optimizely-sdk/lib/optimizely/index.ts +++ b/packages/optimizely-sdk/lib/optimizely/index.ts @@ -1,5 +1,5 @@ /**************************************************************************** - * Copyright 2020-2021, Optimizely, Inc. and contributors * + * Copyright 2020-2022, Optimizely, Inc. and contributors * * * * Licensed under the Apache License, Version 2.0 (the "License"); * * you may not use this file except in compliance with the License. * @@ -1481,7 +1481,7 @@ export default class Optimizely { const allDecideOptions = this.getAllDecideOptions(options); - const forcedDecisionResponse = user.findValidatedForcedDecision(configObj, key); + const forcedDecisionResponse = this.decisionService.findValidatedForcedDecision(configObj, user, key); reasons.push(...forcedDecisionResponse.reasons); const variation = forcedDecisionResponse.result; if (variation) { diff --git a/packages/optimizely-sdk/lib/optimizely_user_context/index.tests.js b/packages/optimizely-sdk/lib/optimizely_user_context/index.tests.js index a7d49d9a0..b6542f6e3 100644 --- a/packages/optimizely-sdk/lib/optimizely_user_context/index.tests.js +++ b/packages/optimizely-sdk/lib/optimizely_user_context/index.tests.js @@ -1,5 +1,5 @@ /**************************************************************************** - * Copyright 2020-2021, Optimizely, Inc. and contributors * + * Copyright 2020-2022, Optimizely, Inc. and contributors * * * * Licensed under the Apache License, Version 2.0 (the "License"); * * you may not use this file except in compliance with the License. * @@ -372,11 +372,13 @@ describe('lib/optimizely_user_context', function() { notificationCenter, }); + sinon.stub(optlyInstance.decisionService.logger, 'log') sinon.stub(eventDispatcher, 'dispatchEvent'); sinon.stub(optlyInstance.notificationCenter, 'sendNotifications'); }); afterEach(function() { + optlyInstance.decisionService.logger.log.restore(); eventDispatcher.dispatchEvent.restore(); optlyInstance.notificationCenter.sendNotifications.restore(); }); @@ -610,17 +612,13 @@ describe('lib/optimizely_user_context', function() { assert.deepEqual(decision.userContext.forcedDecisionsMap[featureKey][ruleKey], { variationKey }); sinon.assert.called(stubLogHandler.log); - var logMessage = stubLogHandler.log.args[4][1]; - assert.strictEqual( - logMessage, - sprintf( - LOG_MESSAGES.USER_HAS_FORCED_DECISION_WITH_RULE_SPECIFIED, - variationKey, - featureKey, - ruleKey, - userId, - ) - ); + var logMessage = optlyInstance.decisionService.logger.log.args[4]; + assert.strictEqual(logMessage[0], 2); + assert.strictEqual(logMessage[1], 'Variation (%s) is mapped to flag (%s), rule (%s) and user (%s) in the forced decision map.'); + assert.strictEqual(logMessage[2], variationKey); + assert.strictEqual(logMessage[3], featureKey); + assert.strictEqual(logMessage[4], ruleKey); + assert.strictEqual(logMessage[5], userId); sinon.assert.calledOnce(eventDispatcher.dispatchEvent); var callArgs = eventDispatcher.dispatchEvent.getCalls()[0].args; diff --git a/packages/optimizely-sdk/lib/optimizely_user_context/index.ts b/packages/optimizely-sdk/lib/optimizely_user_context/index.ts index d8b81cc41..2fcf78238 100644 --- a/packages/optimizely-sdk/lib/optimizely_user_context/index.ts +++ b/packages/optimizely-sdk/lib/optimizely_user_context/index.ts @@ -1,5 +1,5 @@ /**************************************************************************** - * Copyright 2020-2021, Optimizely, Inc. and contributors * + * Copyright 2020-2022, Optimizely, Inc. and contributors * * * * Licensed under the Apache License, Version 2.0 (the "License"); * * you may not use this file except in compliance with the License. * @@ -13,8 +13,6 @@ * See the License for the specific language governing permissions and * * limitations under the License. * ***************************************************************************/ -import { getLogger } from '@optimizely/js-sdk-logging'; - import Optimizely from '../../lib/optimizely'; import { DecisionResponse, @@ -32,8 +30,6 @@ import { } from '../core/project_config'; import { LOG_MESSAGES, CONTROL_ATTRIBUTES } from '../utils/enums'; -const logger = getLogger(); - export default class OptimizelyUserContext { private optimizely: Optimizely; private userId: string; @@ -213,91 +209,6 @@ export default class OptimizelyUserContext { return null; } - /** - * Finds a validated forced decision for specific flagKey and optional ruleKey. - * @param {ProjectConfig}config A projectConfig. - * @param {string} flagKey A flagKey. - * @param {ruleKey} ruleKey A ruleKey (optional). - * @return {DecisionResponse} DecisionResponse object containing valid variation object and decide reasons. - */ - findValidatedForcedDecision( - config: ProjectConfig, - flagKey: string, - ruleKey?: string, - ): DecisionResponse { - - const decideReasons: (string | number)[][] = []; - const forcedDecision = this.findForcedDecision({ flagKey, ruleKey }); - let variation = null; - let variationKey; - if (config && forcedDecision) { - variationKey = forcedDecision.variationKey; - variation = getFlagVariationByKey(config, flagKey, variationKey); - if (variation) { - if (ruleKey) { - logger.info( - LOG_MESSAGES.USER_HAS_FORCED_DECISION_WITH_RULE_SPECIFIED, - variationKey, - flagKey, - ruleKey, - this.userId - ); - decideReasons.push([ - LOG_MESSAGES.USER_HAS_FORCED_DECISION_WITH_RULE_SPECIFIED, - variationKey, - flagKey, - ruleKey, - this.userId - ]); - } else { - logger.info( - LOG_MESSAGES.USER_HAS_FORCED_DECISION_WITH_NO_RULE_SPECIFIED, - variationKey, - flagKey, - this.userId - ); - decideReasons.push([ - LOG_MESSAGES.USER_HAS_FORCED_DECISION_WITH_NO_RULE_SPECIFIED, - variationKey, - flagKey, - this.userId - ]) - } - } else { - if (ruleKey) { - logger.info( - LOG_MESSAGES.USER_HAS_FORCED_DECISION_WITH_RULE_SPECIFIED_BUT_INVALID, - flagKey, - ruleKey, - this.userId - ); - decideReasons.push([ - LOG_MESSAGES.USER_HAS_FORCED_DECISION_WITH_RULE_SPECIFIED_BUT_INVALID, - flagKey, - ruleKey, - this.userId - ]); - } else { - logger.info( - LOG_MESSAGES.USER_HAS_FORCED_DECISION_WITH_NO_RULE_SPECIFIED_BUT_INVALID, - flagKey, - this.userId - ); - decideReasons.push([ - LOG_MESSAGES.USER_HAS_FORCED_DECISION_WITH_NO_RULE_SPECIFIED_BUT_INVALID, - flagKey, - this.userId - ]) - } - } - } - - return { - result: variation, - reasons: decideReasons, - } - } - private cloneUserContext(): OptimizelyUserContext { const userContext = new OptimizelyUserContext({ optimizely: this.getOptimizely(), diff --git a/packages/optimizely-sdk/lib/shared_types.ts b/packages/optimizely-sdk/lib/shared_types.ts index 4c88af5d8..3d8d8dfe9 100644 --- a/packages/optimizely-sdk/lib/shared_types.ts +++ b/packages/optimizely-sdk/lib/shared_types.ts @@ -1,5 +1,5 @@ /** - * Copyright 2020-2021, Optimizely + * Copyright 2020-2022, Optimizely * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -382,11 +382,6 @@ export interface OptimizelyUserContext { options: OptimizelyDecideOption[], ): { [key: string]: OptimizelyDecision }; trackEvent(eventName: string, eventTags?: EventTags): void; - findValidatedForcedDecision( - config: ProjectConfig, - flagKey: string, - ruleKey?: string - ): DecisionResponse; setForcedDecision(context: OptimizelyDecisionContext, decision: OptimizelyForcedDecision): boolean; getForcedDecision(context: OptimizelyDecisionContext): OptimizelyForcedDecision | null; removeForcedDecision(context: OptimizelyDecisionContext): boolean;