Skip to content

Commit d32c8b0

Browse files
authored
feat: Convert condition_tree_evaluator to TS (#560)
* Convert condition_tree_evaluator to TS * Convert audience_evaluator module to TS * Remove console.log * Define conditions and leafEvaluator types in condition_tree_evaluator * Fix comments * Rebase * Define types in audience_evaluator * Revert audience_evaluator to js
1 parent f9d96e6 commit d32c8b0

File tree

5 files changed

+135
-129
lines changed

5 files changed

+135
-129
lines changed

packages/optimizely-sdk/lib/core/audience_evaluator/index.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ import {
2222
LOG_MESSAGES,
2323
ERROR_MESSAGES,
2424
} from '../../utils/enums';
25-
import conditionTreeEvaluator from '../condition_tree_evaluator';
25+
import * as conditionTreeEvaluator from '../condition_tree_evaluator';
2626
import * as customAttributeConditionEvaluator from '../custom_attribute_condition_evaluator';
2727

2828
var logger = getLogger();

packages/optimizely-sdk/lib/core/audience_evaluator/index.tests.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ import { assert } from 'chai';
1818
import { getLogger } from '@optimizely/js-sdk-logging';
1919

2020
import AudienceEvaluator from './index';
21-
import conditionTreeEvaluator from '../condition_tree_evaluator';
21+
import * as conditionTreeEvaluator from '../condition_tree_evaluator';
2222
import * as customAttributeConditionEvaluator from '../custom_attribute_condition_evaluator';
2323

2424
var mockLogger = getLogger();

packages/optimizely-sdk/lib/core/condition_tree_evaluator/index.js

Lines changed: 0 additions & 126 deletions
This file was deleted.

packages/optimizely-sdk/lib/core/condition_tree_evaluator/index.tests.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@
1616
import sinon from 'sinon';
1717
import { assert } from 'chai';
1818

19-
import conditionTreeEvaluator from './';
19+
import * as conditionTreeEvaluator from './';
2020

2121
var conditionA = {
2222
name: 'browser_type',
Lines changed: 132 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,132 @@
1+
/****************************************************************************
2+
* Copyright 2018, 2020, Optimizely, Inc. and contributors *
3+
* *
4+
* Licensed under the Apache License, Version 2.0 (the "License"); *
5+
* you may not use this file except in compliance with the License. *
6+
* You may obtain a copy of the License at *
7+
* *
8+
* http://www.apache.org/licenses/LICENSE-2.0 *
9+
* *
10+
* Unless required by applicable law or agreed to in writing, software *
11+
* distributed under the License is distributed on an "AS IS" BASIS, *
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. *
13+
* See the License for the specific language governing permissions and *
14+
* limitations under the License. *
15+
***************************************************************************/
16+
17+
const AND_CONDITION = 'and';
18+
const OR_CONDITION = 'or';
19+
const NOT_CONDITION = 'not';
20+
21+
const DEFAULT_OPERATOR_TYPES = [AND_CONDITION, OR_CONDITION, NOT_CONDITION];
22+
23+
type ConditionTree<Leaf> = Leaf | unknown[];
24+
25+
type LeafEvaluator<Leaf> = (leaf: Leaf) => boolean | null;
26+
27+
/**
28+
* Top level method to evaluate conditions
29+
* @param {ConditionTree<Leaf>} conditions Nested array of and/or conditions, or a single leaf
30+
* condition value of any type
31+
* Example: ['and', '0', ['or', '1', '2']]
32+
* @param {LeafEvaluator<Leaf>} leafEvaluator Function which will be called to evaluate leaf condition
33+
* values
34+
* @return {?boolean} Result of evaluating the conditions using the operator
35+
* rules and the leaf evaluator. A return value of null
36+
* indicates that the conditions are invalid or unable to be
37+
* evaluated.
38+
*/
39+
export function evaluate<Leaf>(conditions: ConditionTree<Leaf>, leafEvaluator: LeafEvaluator<Leaf>): boolean | null {
40+
if (Array.isArray(conditions)) {
41+
let firstOperator = conditions[0];
42+
let restOfConditions = conditions.slice(1);
43+
44+
if (typeof firstOperator === 'string' && DEFAULT_OPERATOR_TYPES.indexOf(firstOperator) === -1) {
45+
// Operator to apply is not explicit - assume 'or'
46+
firstOperator = OR_CONDITION;
47+
restOfConditions = conditions;
48+
}
49+
50+
switch (firstOperator) {
51+
case AND_CONDITION:
52+
return andEvaluator(restOfConditions, leafEvaluator);
53+
case NOT_CONDITION:
54+
return notEvaluator(restOfConditions, leafEvaluator);
55+
default:
56+
// firstOperator is OR_CONDITION
57+
return orEvaluator(restOfConditions, leafEvaluator);
58+
}
59+
}
60+
61+
const leafCondition = conditions;
62+
return leafEvaluator(leafCondition);
63+
}
64+
65+
/**
66+
* Evaluates an array of conditions as if the evaluator had been applied
67+
* to each entry and the results AND-ed together.
68+
* @param {unknown[]} conditions Array of conditions ex: [operand_1, operand_2]
69+
* @param {LeafEvaluator<Leaf>} leafEvaluator Function which will be called to evaluate leaf condition values
70+
* @return {?boolean} Result of evaluating the conditions. A return value of null
71+
* indicates that the conditions are invalid or unable to be
72+
* evaluated.
73+
*/
74+
function andEvaluator<Leaf>(conditions: ConditionTree<Leaf>, leafEvaluator: LeafEvaluator<Leaf>): boolean | null {
75+
let sawNullResult = false;
76+
if (Array.isArray(conditions)) {
77+
for (let i = 0; i < conditions.length; i++) {
78+
const conditionResult = evaluate(conditions[i] as ConditionTree<Leaf>, leafEvaluator);
79+
if (conditionResult === false) {
80+
return false;
81+
}
82+
if (conditionResult === null) {
83+
sawNullResult = true;
84+
}
85+
}
86+
return sawNullResult ? null : true;
87+
}
88+
return null;
89+
}
90+
91+
/**
92+
* Evaluates an array of conditions as if the evaluator had been applied
93+
* to a single entry and NOT was applied to the result.
94+
* @param {unknown[]} conditions Array of conditions ex: [operand_1]
95+
* @param {LeafEvaluator<Leaf>} leafEvaluator Function which will be called to evaluate leaf condition values
96+
* @return {?boolean} Result of evaluating the conditions. A return value of null
97+
* indicates that the conditions are invalid or unable to be
98+
* evaluated.
99+
*/
100+
function notEvaluator<Leaf>(conditions: ConditionTree<Leaf>, leafEvaluator: LeafEvaluator<Leaf>): boolean | null {
101+
if (Array.isArray(conditions) && conditions.length > 0) {
102+
const result = evaluate(conditions[0] as ConditionTree<Leaf>, leafEvaluator);
103+
return result === null ? null : !result;
104+
}
105+
return null;
106+
}
107+
108+
/**
109+
* Evaluates an array of conditions as if the evaluator had been applied
110+
* to each entry and the results OR-ed together.
111+
* @param {unknown[]} conditions Array of conditions ex: [operand_1, operand_2]
112+
* @param {LeafEvaluator<Leaf>} leafEvaluator Function which will be called to evaluate leaf condition values
113+
* @return {?boolean} Result of evaluating the conditions. A return value of null
114+
* indicates that the conditions are invalid or unable to be
115+
* evaluated.
116+
*/
117+
function orEvaluator<Leaf>(conditions: ConditionTree<Leaf>, leafEvaluator: LeafEvaluator<Leaf>): boolean | null {
118+
let sawNullResult = false;
119+
if (Array.isArray(conditions)) {
120+
for (let i = 0; i < conditions.length; i++) {
121+
const conditionResult = evaluate(conditions[i] as ConditionTree<Leaf>, leafEvaluator);
122+
if (conditionResult === true) {
123+
return true;
124+
}
125+
if (conditionResult === null) {
126+
sawNullResult = true;
127+
}
128+
}
129+
return sawNullResult ? null : false;
130+
}
131+
return null;
132+
}

0 commit comments

Comments
 (0)