UNPKG

7.9 kBJavaScriptView Raw
1'use strict';
2
3Object.defineProperty(exports, "__esModule", {
4 value: true
5});
6
7var _typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol" ? function (obj) { return typeof obj; } : function (obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; };
8
9var _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; }; }();
10
11var _fact = require('./fact');
12
13var _fact2 = _interopRequireDefault(_fact);
14
15var _errors = require('./errors');
16
17var _debug = require('./debug');
18
19var _debug2 = _interopRequireDefault(_debug);
20
21var _jsonpathPlus = require('jsonpath-plus');
22
23var _lodash = require('lodash.isobjectlike');
24
25var _lodash2 = _interopRequireDefault(_lodash);
26
27function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
28
29function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
30
31function defaultPathResolver(value, path) {
32 return (0, _jsonpathPlus.JSONPath)({ path: path, json: value, wrap: false });
33}
34
35/**
36 * Fact results lookup
37 * Triggers fact computations and saves the results
38 * A new almanac is used for every engine run()
39 */
40
41var Almanac = function () {
42 function Almanac(factMap) {
43 var runtimeFacts = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
44 var options = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {};
45
46 _classCallCheck(this, Almanac);
47
48 this.factMap = new Map(factMap);
49 this.factResultsCache = new Map(); // { cacheKey: Promise<factValu> }
50 this.allowUndefinedFacts = Boolean(options.allowUndefinedFacts);
51 this.pathResolver = options.pathResolver || defaultPathResolver;
52 this.events = { success: [], failure: [] };
53 this.ruleResults = [];
54
55 for (var factId in runtimeFacts) {
56 var fact = void 0;
57 if (runtimeFacts[factId] instanceof _fact2.default) {
58 fact = runtimeFacts[factId];
59 } else {
60 fact = new _fact2.default(factId, runtimeFacts[factId]);
61 }
62
63 this._addConstantFact(fact);
64 (0, _debug2.default)('almanac::constructor initialized runtime fact:' + fact.id + ' with ' + fact.value + '<' + _typeof(fact.value) + '>');
65 }
66 }
67
68 /**
69 * Adds a success event
70 * @param {Object} event
71 */
72
73
74 _createClass(Almanac, [{
75 key: 'addEvent',
76 value: function addEvent(event, outcome) {
77 if (!outcome) throw new Error('outcome required: "success" | "failure"]');
78 this.events[outcome].push(event);
79 }
80
81 /**
82 * retrieve successful events
83 */
84
85 }, {
86 key: 'getEvents',
87 value: function getEvents() {
88 var outcome = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : '';
89
90 if (outcome) return this.events[outcome];
91 return this.events.success.concat(this.events.failure);
92 }
93
94 /**
95 * Adds a rule result
96 * @param {Object} event
97 */
98
99 }, {
100 key: 'addResult',
101 value: function addResult(ruleResult) {
102 this.ruleResults.push(ruleResult);
103 }
104
105 /**
106 * retrieve successful events
107 */
108
109 }, {
110 key: 'getResults',
111 value: function getResults() {
112 return this.ruleResults;
113 }
114
115 /**
116 * Retrieve fact by id, raising an exception if it DNE
117 * @param {String} factId
118 * @return {Fact}
119 */
120
121 }, {
122 key: '_getFact',
123 value: function _getFact(factId) {
124 return this.factMap.get(factId);
125 }
126
127 /**
128 * Registers fact with the almanac
129 * @param {[type]} fact [description]
130 */
131
132 }, {
133 key: '_addConstantFact',
134 value: function _addConstantFact(fact) {
135 this.factMap.set(fact.id, fact);
136 this._setFactValue(fact, {}, fact.value);
137 }
138
139 /**
140 * Sets the computed value of a fact
141 * @param {Fact} fact
142 * @param {Object} params - values for differentiating this fact value from others, used for cache key
143 * @param {Mixed} value - computed value
144 */
145
146 }, {
147 key: '_setFactValue',
148 value: function _setFactValue(fact, params, value) {
149 var cacheKey = fact.getCacheKey(params);
150 var factValue = Promise.resolve(value);
151 if (cacheKey) {
152 this.factResultsCache.set(cacheKey, factValue);
153 }
154 return factValue;
155 }
156
157 /**
158 * Adds a constant fact during runtime. Can be used mid-run() to add additional information
159 * @param {String} fact - fact identifier
160 * @param {Mixed} value - constant value of the fact
161 */
162
163 }, {
164 key: 'addRuntimeFact',
165 value: function addRuntimeFact(factId, value) {
166 (0, _debug2.default)('almanac::addRuntimeFact id:' + factId);
167 var fact = new _fact2.default(factId, value);
168 return this._addConstantFact(fact);
169 }
170
171 /**
172 * Returns the value of a fact, based on the given parameters. Utilizes the 'almanac' maintained
173 * by the engine, which cache's fact computations based on parameters provided
174 * @param {string} factId - fact identifier
175 * @param {Object} params - parameters to feed into the fact. By default, these will also be used to compute the cache key
176 * @param {String} path - object
177 * @return {Promise} a promise which will resolve with the fact computation.
178 */
179
180 }, {
181 key: 'factValue',
182 value: function factValue(factId) {
183 var _this = this;
184
185 var params = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
186 var path = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : '';
187
188 var factValuePromise = void 0;
189 var fact = this._getFact(factId);
190 if (fact === undefined) {
191 if (this.allowUndefinedFacts) {
192 return Promise.resolve(undefined);
193 } else {
194 return Promise.reject(new _errors.UndefinedFactError('Undefined fact: ' + factId));
195 }
196 }
197 if (fact.isConstant()) {
198 factValuePromise = Promise.resolve(fact.calculate(params, this));
199 } else {
200 var cacheKey = fact.getCacheKey(params);
201 var cacheVal = cacheKey && this.factResultsCache.get(cacheKey);
202 if (cacheVal) {
203 factValuePromise = Promise.resolve(cacheVal);
204 (0, _debug2.default)('almanac::factValue cache hit for fact:' + factId);
205 } else {
206 (0, _debug2.default)('almanac::factValue cache miss for fact:' + factId + '; calculating');
207 factValuePromise = this._setFactValue(fact, params, fact.calculate(params, this));
208 }
209 }
210 if (path) {
211 (0, _debug2.default)('condition::evaluate extracting object property ' + path);
212 return factValuePromise.then(function (factValue) {
213 if ((0, _lodash2.default)(factValue)) {
214 var pathValue = _this.pathResolver(factValue, path);
215 (0, _debug2.default)('condition::evaluate extracting object property ' + path + ', received: ' + JSON.stringify(pathValue));
216 return pathValue;
217 } else {
218 (0, _debug2.default)('condition::evaluate could not compute object path(' + path + ') of non-object: ' + factValue + ' <' + (typeof factValue === 'undefined' ? 'undefined' : _typeof(factValue)) + '>; continuing with ' + factValue);
219 return factValue;
220 }
221 });
222 }
223
224 return factValuePromise;
225 }
226 }]);
227
228 return Almanac;
229}();
230
231exports.default = Almanac;
\No newline at end of file