Skip to content

Commit 299e1d7

Browse files
authored
[FSSDK-11494] fix UserAttributes type (#1042)
1 parent 32f857e commit 299e1d7

File tree

7 files changed

+46
-35
lines changed

7 files changed

+46
-35
lines changed

lib/core/decision_service/index.spec.ts

+6-6
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ import OptimizelyUserContext from '../../optimizely_user_context';
2020
import { bucket } from '../bucketer';
2121
import { getTestProjectConfig, getTestProjectConfigWithFeatures } from '../../tests/test_data';
2222
import { createProjectConfig, ProjectConfig } from '../../project_config/project_config';
23-
import { BucketerParams, Experiment, OptimizelyDecideOption, UserProfile } from '../../shared_types';
23+
import { BucketerParams, Experiment, OptimizelyDecideOption, UserAttributes, UserProfile } from '../../shared_types';
2424
import { CONTROL_ATTRIBUTES, DECISION_SOURCES } from '../../utils/enums';
2525
import { getDecisionTestDatafile } from '../../tests/decision_test_datafile';
2626
import { Value } from '../../utils/promise/operation_value';
@@ -344,7 +344,7 @@ describe('DecisionService', () => {
344344
const config = createProjectConfig(cloneDeep(testData));
345345
const experiment = config.experimentIdMap['111127'];
346346

347-
const attributes: any = {
347+
const attributes: UserAttributes = {
348348
$opt_experiment_bucket_map: {
349349
'111127': {
350350
variation_id: '111129', // ID of the 'variation' variation
@@ -682,7 +682,7 @@ describe('DecisionService', () => {
682682
const config = createProjectConfig(cloneDeep(testData));
683683
const experiment = config.experimentIdMap['111127'];
684684

685-
const attributes: any = {
685+
const attributes: UserAttributes = {
686686
$opt_experiment_bucket_map: {
687687
'111127': {
688688
variation_id: '111129', // ID of the 'variation' variation
@@ -715,7 +715,7 @@ describe('DecisionService', () => {
715715
const config = createProjectConfig(cloneDeep(testData));
716716
const experiment = config.experimentIdMap['111127'];
717717

718-
const attributes: any = {
718+
const attributes: UserAttributes = {
719719
$opt_experiment_bucket_map: {
720720
'122227': {
721721
variation_id: '111129', // ID of the 'variation' variation
@@ -748,7 +748,7 @@ describe('DecisionService', () => {
748748
const config = createProjectConfig(cloneDeep(testData));
749749
const experiment = config.experimentIdMap['111127'];
750750

751-
const attributes: any = {
751+
const attributes: UserAttributes = {
752752
$opt_experiment_bucket_map: {
753753
'111127': {
754754
variation_id: '111129', // ID of the 'variation' variation
@@ -774,7 +774,7 @@ describe('DecisionService', () => {
774774
const config = createProjectConfig(cloneDeep(testData));
775775
const experiment = config.experimentIdMap['111127'];
776776

777-
const attributes: any = {
777+
const attributes: UserAttributes = {
778778
$opt_experiment_bucket_map: {
779779
'111127': {
780780
variation_id: '111129', // ID of the 'variation' variation

lib/event_processor/event_builder/log_event.ts

+4-3
Original file line numberDiff line numberDiff line change
@@ -15,12 +15,13 @@
1515
*/
1616
import { ConversionEvent, ImpressionEvent, UserEvent } from './user_event';
1717

18+
import { CONTROL_ATTRIBUTES } from '../../utils/enums';
19+
1820
import { LogEvent } from '../event_dispatcher/event_dispatcher';
1921
import { EventTags } from '../../shared_types';
2022

2123
const ACTIVATE_EVENT_KEY = 'campaign_activated'
2224
const CUSTOM_ATTRIBUTE_FEATURE_TYPE = 'custom'
23-
const BOT_FILTERING_KEY = '$opt_bot_filtering'
2425

2526
export type EventBatch = {
2627
account_id: string
@@ -204,8 +205,8 @@ function makeVisitor(data: ImpressionEvent | ConversionEvent): Visitor {
204205

205206
if (typeof data.context.botFiltering === 'boolean') {
206207
visitor.attributes.push({
207-
entity_id: BOT_FILTERING_KEY,
208-
key: BOT_FILTERING_KEY,
208+
entity_id: CONTROL_ATTRIBUTES.BOT_FILTERING,
209+
key: CONTROL_ATTRIBUTES.BOT_FILTERING,
209210
type: CUSTOM_ATTRIBUTE_FEATURE_TYPE,
210211
value: data.context.botFiltering,
211212
})

lib/event_processor/event_builder/user_event.ts

+22-15
Original file line numberDiff line numberDiff line change
@@ -254,23 +254,30 @@ const buildVisitorAttributes = (
254254
attributes?: UserAttributes,
255255
logger?: LoggerFacade
256256
): VisitorAttribute[] => {
257-
const builtAttributes: VisitorAttribute[] = [];
257+
if (!attributes) {
258+
return [];
259+
}
260+
258261
// Omit attribute values that are not supported by the log endpoint.
259-
if (attributes) {
260-
Object.keys(attributes || {}).forEach(function(attributeKey) {
261-
const attributeValue = attributes[attributeKey];
262-
if (isAttributeValid(attributeKey, attributeValue)) {
263-
const attributeId = getAttributeId(configObj, attributeKey, logger);
264-
if (attributeId) {
265-
builtAttributes.push({
266-
entityId: attributeId,
267-
key: attributeKey,
268-
value: attributeValue!,
269-
});
270-
}
262+
const builtAttributes: VisitorAttribute[] = [];
263+
Object.keys(attributes).forEach(function(attributeKey) {
264+
const attributeValue = attributes[attributeKey];
265+
266+
if (typeof attributeValue === 'object' || typeof attributeValue === 'undefined') {
267+
return;
268+
}
269+
270+
if (isAttributeValid(attributeKey, attributeValue)) {
271+
const attributeId = getAttributeId(configObj, attributeKey, logger);
272+
if (attributeId) {
273+
builtAttributes.push({
274+
entityId: attributeId,
275+
key: attributeKey,
276+
value: attributeValue,
277+
});
271278
}
272-
});
273-
}
279+
}
280+
});
274281

275282
return builtAttributes;
276283
}

lib/optimizely_user_context/index.tests.js

+6-5
Original file line numberDiff line numberDiff line change
@@ -20,12 +20,13 @@ import { NOTIFICATION_TYPES } from '../notification_center/type';
2020
import OptimizelyUserContext from './';
2121
import { createNotificationCenter } from '../notification_center';
2222
import Optimizely from '../optimizely';
23-
import { CONTROL_ATTRIBUTES, LOG_LEVEL } from '../utils/enums';
23+
import { LOG_LEVEL } from '../utils/enums';
2424
import testData from '../tests/test_data';
2525
import { OptimizelyDecideOption } from '../shared_types';
2626
import { getMockProjectConfigManager } from '../tests/mock/mock_project_config_manager';
2727
import { createProjectConfig } from '../project_config/project_config';
2828
import { getForwardingEventProcessor } from '../event_processor/forwarding_event_processor';
29+
import { FORCED_DECISION_NULL_RULE_KEY } from './index'
2930

3031
import {
3132
USER_HAS_FORCED_DECISION_WITH_NO_RULE_SPECIFIED,
@@ -449,7 +450,7 @@ describe('lib/optimizely_user_context', function() {
449450
assert.deepEqual(decision.userContext.getAttributes(), {});
450451
assert.deepEqual(Object.keys(decision.userContext.forcedDecisionsMap).length, 1);
451452
assert.deepEqual(
452-
decision.userContext.forcedDecisionsMap[featureKey][CONTROL_ATTRIBUTES.FORCED_DECISION_NULL_RULE_KEY],
453+
decision.userContext.forcedDecisionsMap[featureKey][FORCED_DECISION_NULL_RULE_KEY],
453454
{ variationKey }
454455
);
455456
assert.equal(
@@ -475,7 +476,7 @@ describe('lib/optimizely_user_context', function() {
475476
assert.deepEqual(decision.userContext.getAttributes(), {});
476477
assert.deepEqual(Object.keys(decision.userContext.forcedDecisionsMap).length, 1);
477478
assert.deepEqual(
478-
decision.userContext.forcedDecisionsMap[featureKey][CONTROL_ATTRIBUTES.FORCED_DECISION_NULL_RULE_KEY],
479+
decision.userContext.forcedDecisionsMap[featureKey][FORCED_DECISION_NULL_RULE_KEY],
479480
{ variationKey }
480481
);
481482
assert.equal(
@@ -509,7 +510,7 @@ describe('lib/optimizely_user_context', function() {
509510
assert.deepEqual(decision.userContext.getAttributes(), {});
510511
assert.deepEqual(Object.values(decision.userContext.forcedDecisionsMap).length, 1);
511512
assert.deepEqual(
512-
decision.userContext.forcedDecisionsMap[featureKey][CONTROL_ATTRIBUTES.FORCED_DECISION_NULL_RULE_KEY],
513+
decision.userContext.forcedDecisionsMap[featureKey][FORCED_DECISION_NULL_RULE_KEY],
513514
{ variationKey }
514515
);
515516
assert.equal(
@@ -776,7 +777,7 @@ describe('lib/optimizely_user_context', function() {
776777
assert.equal(decision.ruleKey, '18322080788');
777778
assert.deepEqual(Object.keys(decision.userContext.forcedDecisionsMap).length, 1);
778779
assert.deepEqual(
779-
decision.userContext.forcedDecisionsMap[featureKey][CONTROL_ATTRIBUTES.FORCED_DECISION_NULL_RULE_KEY],
780+
decision.userContext.forcedDecisionsMap[featureKey][FORCED_DECISION_NULL_RULE_KEY],
780781
{ variationKey }
781782
);
782783
assert.equal(

lib/optimizely_user_context/index.ts

+5-4
Original file line numberDiff line numberDiff line change
@@ -23,9 +23,10 @@ import {
2323
UserAttributeValue,
2424
UserAttributes,
2525
} from '../shared_types';
26-
import { CONTROL_ATTRIBUTES } from '../utils/enums';
2726
import { OptimizelySegmentOption } from '../odp/segment_manager/optimizely_segment_option';
2827

28+
export const FORCED_DECISION_NULL_RULE_KEY = '$opt_null_rule_key';
29+
2930
interface OptimizelyUserContextConfig {
3031
optimizely: Optimizely;
3132
userId: string;
@@ -142,7 +143,7 @@ export default class OptimizelyUserContext implements IOptimizelyUserContext {
142143
setForcedDecision(context: OptimizelyDecisionContext, decision: OptimizelyForcedDecision): boolean {
143144
const flagKey = context.flagKey;
144145

145-
const ruleKey = context.ruleKey ?? CONTROL_ATTRIBUTES.FORCED_DECISION_NULL_RULE_KEY;
146+
const ruleKey = context.ruleKey ?? FORCED_DECISION_NULL_RULE_KEY;
146147
const variationKey = decision.variationKey;
147148
const forcedDecision = { variationKey };
148149

@@ -169,7 +170,7 @@ export default class OptimizelyUserContext implements IOptimizelyUserContext {
169170
* @return {boolean} true if the forced decision has been removed successfully
170171
*/
171172
removeForcedDecision(context: OptimizelyDecisionContext): boolean {
172-
const ruleKey = context.ruleKey ?? CONTROL_ATTRIBUTES.FORCED_DECISION_NULL_RULE_KEY;
173+
const ruleKey = context.ruleKey ?? FORCED_DECISION_NULL_RULE_KEY;
173174
const flagKey = context.flagKey;
174175

175176
let isForcedDecisionRemoved = false;
@@ -204,7 +205,7 @@ export default class OptimizelyUserContext implements IOptimizelyUserContext {
204205
*/
205206
private findForcedDecision(context: OptimizelyDecisionContext): OptimizelyForcedDecision | null {
206207
let variationKey;
207-
const validRuleKey = context.ruleKey ?? CONTROL_ATTRIBUTES.FORCED_DECISION_NULL_RULE_KEY;
208+
const validRuleKey = context.ruleKey ?? FORCED_DECISION_NULL_RULE_KEY;
208209
const flagKey = context.flagKey;
209210

210211
if (this.forcedDecisionsMap.hasOwnProperty(context.flagKey)) {

lib/shared_types.ts

+3-1
Original file line numberDiff line numberDiff line change
@@ -72,9 +72,11 @@ export interface DecisionResponse<T> {
7272
readonly reasons: [string, ...any[]][];
7373
}
7474

75-
export type UserAttributeValue = string | number | boolean | null;
75+
export type UserAttributeValue = string | number | boolean | null | undefined | ExperimentBucketMap;
7676

7777
export type UserAttributes = {
78+
$opt_bucketing_id?: string;
79+
$opt_experiment_bucket_map?: ExperimentBucketMap;
7880
[name: string]: UserAttributeValue;
7981
};
8082

lib/utils/enums/index.ts

-1
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,6 @@ export const CONTROL_ATTRIBUTES = {
3636
BUCKETING_ID: '$opt_bucketing_id',
3737
STICKY_BUCKETING_KEY: '$opt_experiment_bucket_map',
3838
USER_AGENT: '$opt_user_agent',
39-
FORCED_DECISION_NULL_RULE_KEY: '$opt_null_rule_key',
4039
};
4140

4241
export const JAVASCRIPT_CLIENT_ENGINE = 'javascript-sdk';

0 commit comments

Comments
 (0)