1 | 'use strict';
|
2 |
|
3 | Object.defineProperty(exports, "__esModule", {
|
4 | value: true
|
5 | });
|
6 |
|
7 | var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }();
|
8 |
|
9 | var _condition = require('./condition');
|
10 |
|
11 | var _condition2 = _interopRequireDefault(_condition);
|
12 |
|
13 | var _ruleResult = require('./rule-result');
|
14 |
|
15 | var _ruleResult2 = _interopRequireDefault(_ruleResult);
|
16 |
|
17 | var _events = require('events');
|
18 |
|
19 | var _debug = require('./debug');
|
20 |
|
21 | var _debug2 = _interopRequireDefault(_debug);
|
22 |
|
23 | function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
|
24 |
|
25 | function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
|
26 |
|
27 | function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; }
|
28 |
|
29 | function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; }
|
30 |
|
31 | var Rule = function (_EventEmitter) {
|
32 | _inherits(Rule, _EventEmitter);
|
33 |
|
34 | |
35 |
|
36 |
|
37 |
|
38 |
|
39 |
|
40 |
|
41 |
|
42 |
|
43 |
|
44 |
|
45 | function Rule(options) {
|
46 | _classCallCheck(this, Rule);
|
47 |
|
48 | var _this = _possibleConstructorReturn(this, (Rule.__proto__ || Object.getPrototypeOf(Rule)).call(this));
|
49 |
|
50 | if (typeof options === 'string') {
|
51 | options = JSON.parse(options);
|
52 | }
|
53 | if (options && options.conditions) {
|
54 | _this.setConditions(options.conditions);
|
55 | }
|
56 | if (options && options.onSuccess) {
|
57 | _this.on('success', options.onSuccess);
|
58 | }
|
59 | if (options && options.onFailure) {
|
60 | _this.on('failure', options.onFailure);
|
61 | }
|
62 | if (options && (options.name || options.name === 0)) {
|
63 | _this.setName(options.name);
|
64 | }
|
65 |
|
66 | var priority = options && options.priority || 1;
|
67 | _this.setPriority(priority);
|
68 |
|
69 | var event = options && options.event || { type: 'unknown' };
|
70 | _this.setEvent(event);
|
71 | return _this;
|
72 | }
|
73 |
|
74 | |
75 |
|
76 |
|
77 |
|
78 |
|
79 |
|
80 | _createClass(Rule, [{
|
81 | key: 'setPriority',
|
82 | value: function setPriority(priority) {
|
83 | priority = parseInt(priority, 10);
|
84 | if (priority <= 0) throw new Error('Priority must be greater than zero');
|
85 | this.priority = priority;
|
86 | return this;
|
87 | }
|
88 |
|
89 | |
90 |
|
91 |
|
92 |
|
93 |
|
94 | }, {
|
95 | key: 'setName',
|
96 | value: function setName(name) {
|
97 | if (!name && name !== 0) {
|
98 | throw new Error('Rule "name" must be defined');
|
99 | }
|
100 | this.name = name;
|
101 | return this;
|
102 | }
|
103 |
|
104 | |
105 |
|
106 |
|
107 |
|
108 |
|
109 | }, {
|
110 | key: 'setConditions',
|
111 | value: function setConditions(conditions) {
|
112 | if (!Object.prototype.hasOwnProperty.call(conditions, 'all') && !Object.prototype.hasOwnProperty.call(conditions, 'any')) {
|
113 | throw new Error('"conditions" root must contain a single instance of "all" or "any"');
|
114 | }
|
115 | this.conditions = new _condition2.default(conditions);
|
116 | return this;
|
117 | }
|
118 |
|
119 | |
120 |
|
121 |
|
122 |
|
123 |
|
124 |
|
125 |
|
126 | }, {
|
127 | key: 'setEvent',
|
128 | value: function setEvent(event) {
|
129 | if (!event) throw new Error('Rule: setEvent() requires event object');
|
130 | if (!Object.prototype.hasOwnProperty.call(event, 'type')) throw new Error('Rule: setEvent() requires event object with "type" property');
|
131 | this.event = {
|
132 | type: event.type
|
133 | };
|
134 | if (event.params) this.event.params = event.params;
|
135 | return this;
|
136 | }
|
137 |
|
138 | |
139 |
|
140 |
|
141 |
|
142 |
|
143 |
|
144 | }, {
|
145 | key: 'setEngine',
|
146 | value: function setEngine(engine) {
|
147 | this.engine = engine;
|
148 | return this;
|
149 | }
|
150 | }, {
|
151 | key: 'toJSON',
|
152 | value: function toJSON() {
|
153 | var stringify = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : true;
|
154 |
|
155 | var props = {
|
156 | conditions: this.conditions.toJSON(false),
|
157 | priority: this.priority,
|
158 | event: this.event,
|
159 | name: this.name
|
160 | };
|
161 | if (stringify) {
|
162 | return JSON.stringify(props);
|
163 | }
|
164 | return props;
|
165 | }
|
166 |
|
167 | |
168 |
|
169 |
|
170 |
|
171 |
|
172 |
|
173 |
|
174 |
|
175 |
|
176 | }, {
|
177 | key: 'prioritizeConditions',
|
178 | value: function prioritizeConditions(conditions) {
|
179 | var _this2 = this;
|
180 |
|
181 | var factSets = conditions.reduce(function (sets, condition) {
|
182 |
|
183 |
|
184 | var priority = condition.priority;
|
185 | if (!priority) {
|
186 | var fact = _this2.engine.getFact(condition.fact);
|
187 | priority = fact && fact.priority || 1;
|
188 | }
|
189 | if (!sets[priority]) sets[priority] = [];
|
190 | sets[priority].push(condition);
|
191 | return sets;
|
192 | }, {});
|
193 | return Object.keys(factSets).sort(function (a, b) {
|
194 | return Number(a) > Number(b) ? -1 : 1;
|
195 | }).map(function (priority) {
|
196 | return factSets[priority];
|
197 | });
|
198 | }
|
199 |
|
200 | |
201 |
|
202 |
|
203 |
|
204 |
|
205 |
|
206 | }, {
|
207 | key: 'evaluate',
|
208 | value: function evaluate(almanac) {
|
209 | var _this3 = this;
|
210 |
|
211 | var ruleResult = new _ruleResult2.default(this.conditions, this.event, this.priority, this.name);
|
212 |
|
213 | |
214 |
|
215 |
|
216 |
|
217 |
|
218 | var evaluateCondition = function evaluateCondition(condition) {
|
219 | if (condition.isBooleanOperator()) {
|
220 | var subConditions = condition[condition.operator];
|
221 | var comparisonPromise = void 0;
|
222 | if (condition.operator === 'all') {
|
223 | comparisonPromise = all(subConditions);
|
224 | } else {
|
225 | comparisonPromise = any(subConditions);
|
226 | }
|
227 |
|
228 | return comparisonPromise.then(function (comparisonValue) {
|
229 | var passes = comparisonValue === true;
|
230 | condition.result = passes;
|
231 | return passes;
|
232 | });
|
233 | } else {
|
234 | return condition.evaluate(almanac, _this3.engine.operators).then(function (evaluationResult) {
|
235 | var passes = evaluationResult.result;
|
236 | condition.factResult = evaluationResult.leftHandSideValue;
|
237 | condition.result = passes;
|
238 | return passes;
|
239 | });
|
240 | }
|
241 | };
|
242 |
|
243 | |
244 |
|
245 |
|
246 |
|
247 |
|
248 |
|
249 | var evaluateConditions = function evaluateConditions(conditions, method) {
|
250 | if (!Array.isArray(conditions)) conditions = [conditions];
|
251 |
|
252 | return Promise.all(conditions.map(function (condition) {
|
253 | return evaluateCondition(condition);
|
254 | })).then(function (conditionResults) {
|
255 | (0, _debug2.default)('rule::evaluateConditions results', conditionResults);
|
256 | return method.call(conditionResults, function (result) {
|
257 | return result === true;
|
258 | });
|
259 | });
|
260 | };
|
261 |
|
262 | |
263 |
|
264 |
|
265 |
|
266 |
|
267 |
|
268 |
|
269 |
|
270 |
|
271 |
|
272 | var prioritizeAndRun = function prioritizeAndRun(conditions, operator) {
|
273 | if (conditions.length === 0) {
|
274 | return Promise.resolve(true);
|
275 | }
|
276 | var method = Array.prototype.some;
|
277 | if (operator === 'all') {
|
278 | method = Array.prototype.every;
|
279 | }
|
280 | var orderedSets = _this3.prioritizeConditions(conditions);
|
281 | var cursor = Promise.resolve();
|
282 |
|
283 |
|
284 | var _loop = function _loop(i) {
|
285 | var set = orderedSets[i];
|
286 | var stop = false;
|
287 | cursor = cursor.then(function (setResult) {
|
288 |
|
289 | if (operator === 'any' && setResult === true || stop) {
|
290 | (0, _debug2.default)('prioritizeAndRun::detected truthy result; skipping remaining conditions');
|
291 | stop = true;
|
292 | return true;
|
293 | }
|
294 |
|
295 |
|
296 | if (operator === 'all' && setResult === false || stop) {
|
297 | (0, _debug2.default)('prioritizeAndRun::detected falsey result; skipping remaining conditions');
|
298 | stop = true;
|
299 | return false;
|
300 | }
|
301 |
|
302 | return evaluateConditions(set, method);
|
303 | });
|
304 | };
|
305 |
|
306 | for (var i = 0; i < orderedSets.length; i++) {
|
307 | _loop(i);
|
308 | }
|
309 | return cursor;
|
310 | };
|
311 |
|
312 | |
313 |
|
314 |
|
315 |
|
316 |
|
317 | var any = function any(conditions) {
|
318 | return prioritizeAndRun(conditions, 'any');
|
319 | };
|
320 |
|
321 | |
322 |
|
323 |
|
324 |
|
325 |
|
326 | var all = function all(conditions) {
|
327 | return prioritizeAndRun(conditions, 'all');
|
328 | };
|
329 |
|
330 | |
331 |
|
332 |
|
333 |
|
334 | var processResult = function processResult(result) {
|
335 | ruleResult.setResult(result);
|
336 |
|
337 | if (result) _this3.emit('success', ruleResult.event, almanac, ruleResult);else _this3.emit('failure', ruleResult.event, almanac, ruleResult);
|
338 | return ruleResult;
|
339 | };
|
340 |
|
341 | if (ruleResult.conditions.any) {
|
342 | return any(ruleResult.conditions.any).then(function (result) {
|
343 | return processResult(result);
|
344 | });
|
345 | } else {
|
346 | return all(ruleResult.conditions.all).then(function (result) {
|
347 | return processResult(result);
|
348 | });
|
349 | }
|
350 | }
|
351 | }]);
|
352 |
|
353 | return Rule;
|
354 | }(_events.EventEmitter);
|
355 |
|
356 | exports.default = Rule; |
\ | No newline at end of file |