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