1 | import _ from 'lodash';
|
2 | import context from './context';
|
3 | import { decide as decideV1 } from './interpreter_v1';
|
4 | import parse from './parse';
|
5 | import { reduceDecisionRules } from './reducer';
|
6 | import semver from 'semver';
|
7 | import { CraftAiDecisionError, CraftAiNullDecisionError } from './errors';
|
8 | import { decide as decideV2, distribution } from './interpreter_v2';
|
9 | import { formatDecisionRules, formatProperty } from './formatter';
|
10 |
|
11 | const DECISION_FORMAT_VERSION = '1.1.0';
|
12 |
|
13 | export function decide(tree, ...args) {
|
14 | const { _version, configuration, trees } = parse(tree);
|
15 | const ctx = configuration ? context(configuration, ...args) : _.extend({}, ...args);
|
16 | return _decide(configuration, trees, ctx, _version);
|
17 | }
|
18 |
|
19 | function _decide(configuration, trees, ctx, _version) {
|
20 | if (semver.satisfies(_version, '>=1.0.0 <2.0.0')) {
|
21 | return decideV1(configuration, trees, ctx);
|
22 | }
|
23 | if (semver.satisfies(_version, '>=2.0.0 <3.0.0')) {
|
24 | return decideV2(configuration, trees, ctx);
|
25 | }
|
26 | throw new CraftAiDecisionError(`Invalid decision tree format, "${_version}" is not a valid version.`);
|
27 | }
|
28 |
|
29 | function reduceNodes(tree, fn, initialAccValue) {
|
30 | let nodes = [];
|
31 | nodes.push(tree);
|
32 | const recursiveNext = (acc) => {
|
33 | if (nodes.length == 0) {
|
34 |
|
35 | return acc;
|
36 | }
|
37 |
|
38 | const node = nodes.pop();
|
39 | if (node.children) {
|
40 | nodes = node.children.concat(nodes);
|
41 | }
|
42 |
|
43 | const updatedAcc = fn(acc, node);
|
44 | return recursiveNext(updatedAcc);
|
45 | };
|
46 | return recursiveNext(initialAccValue);
|
47 | }
|
48 |
|
49 | export function getDecisionRulesProperties(tree) {
|
50 | const { configuration, trees } = parse(tree);
|
51 | return _(trees)
|
52 | .values()
|
53 | .reduce(
|
54 | (properties, tree) => reduceNodes(
|
55 | tree,
|
56 | (properties, node) => {
|
57 | if (node.children) {
|
58 | return properties.concat(node.children[0].decision_rule.property);
|
59 | }
|
60 | return properties;
|
61 | },
|
62 | properties),
|
63 | _([])
|
64 | )
|
65 | .uniq()
|
66 | .map((property) => _.extend(configuration.context[property], {
|
67 | property: property
|
68 | }))
|
69 | .value();
|
70 | }
|
71 |
|
72 | export function decideFromContextsArray(tree, contexts) {
|
73 | const { _version, configuration, trees } = parse(tree);
|
74 | return _.map(contexts, (contextsItem) => {
|
75 | let ctx;
|
76 | if (_.isArray(contextsItem)) {
|
77 | ctx = context(configuration, ...contextsItem);
|
78 | }
|
79 | else {
|
80 | ctx = context(configuration, contextsItem);
|
81 | }
|
82 | try {
|
83 | return _decide(configuration, trees, ctx, _version);
|
84 | }
|
85 | catch (error) {
|
86 | if (error instanceof CraftAiNullDecisionError) {
|
87 | const { message, metadata } = error;
|
88 | return {
|
89 | _version: DECISION_FORMAT_VERSION,
|
90 | context: ctx,
|
91 | error: { message, metadata }
|
92 | };
|
93 | }
|
94 | else {
|
95 | throw error;
|
96 | }
|
97 | }
|
98 | });
|
99 | }
|
100 |
|
101 | export { formatDecisionRules, formatProperty, reduceDecisionRules, distribution };
|