@fibery/expression-utils 1.1.12 → 1.1.13

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -9,11 +9,9 @@ var moment__default = /*#__PURE__*/_interopDefaultLegacy(moment);
9
9
  const serializeDate = momentDate => {
10
10
  return momentDate.format("YYYY-MM-DD");
11
11
  };
12
-
13
12
  const serializeDateTime = momentDate => {
14
13
  return momentDate.toISOString();
15
14
  };
16
-
17
15
  const formulaTodayDateParamPlaceholder = "$formula-today-date-placeholder";
18
16
  const formulaNowDateTimeParamPlaceholder = "$formula-now-date-time-placeholder";
19
17
  const todayDateParamPlaceholder = "$today-date";
@@ -74,13 +72,28 @@ const paramsPlaceholdersLookup = {
74
72
  [yearFromNowStartDateTimeParamPlaceholder]: () => serializeDateTime(moment__default["default"]().add(1, "year").startOf("day")),
75
73
  [yearFromNowEndDateTimeParamPlaceholder]: () => serializeDateTime(moment__default["default"]().add(1, "year").endOf("day"))
76
74
  };
75
+ const dynamicFilterParamPrefix = `$dynamic_`;
76
+ const isDynamicFilterParam = paramValue => paramValue.startsWith(dynamicFilterParamPrefix);
77
+ const getFieldIdFromDynamicParam = paramValue => paramValue.substring(dynamicFilterParamPrefix.length);
78
+ const mapDynamicParams = (params, onDynamicParam) => {
79
+ if (!params) {
80
+ return params;
81
+ }
82
+ return Object.fromEntries(Object.entries(params).map(([key, value]) => {
83
+ if (___default["default"].isArray(value)) {
84
+ return [key, value.map(v => {
85
+ return ___default["default"].isString(v) && isDynamicFilterParam(v) ? onDynamicParam(v) : v;
86
+ })];
87
+ } else {
88
+ return [key, ___default["default"].isString(value) && isDynamicFilterParam(value) ? onDynamicParam(value) : value];
89
+ }
90
+ }));
91
+ };
77
92
  const replacePlaceholdersInParams = params => params && ___default["default"].mapValues(params, (value, key) => {
78
93
  const replaceFn = paramsPlaceholdersLookup[key];
79
-
80
94
  if (replaceFn) {
81
95
  return replaceFn();
82
96
  }
83
-
84
97
  return value;
85
98
  });
86
99
  const dateToDateTimeIntervalLookup = {
@@ -123,8 +136,12 @@ const dateToDateTimeIntervalLookup = {
123
136
  };
124
137
 
125
138
  exports.dateToDateTimeIntervalLookup = dateToDateTimeIntervalLookup;
139
+ exports.dynamicFilterParamPrefix = dynamicFilterParamPrefix;
126
140
  exports.formulaNowDateTimeParamPlaceholder = formulaNowDateTimeParamPlaceholder;
127
141
  exports.formulaTodayDateParamPlaceholder = formulaTodayDateParamPlaceholder;
142
+ exports.getFieldIdFromDynamicParam = getFieldIdFromDynamicParam;
143
+ exports.isDynamicFilterParam = isDynamicFilterParam;
144
+ exports.mapDynamicParams = mapDynamicParams;
128
145
  exports.monthAgoDateParamPlaceholder = monthAgoDateParamPlaceholder;
129
146
  exports.monthAgoEndDateTimeParamPlaceholder = monthAgoEndDateTimeParamPlaceholder;
130
147
  exports.monthAgoStartDateTimeParamPlaceholder = monthAgoStartDateTimeParamPlaceholder;
package/lib/utils.js ADDED
@@ -0,0 +1,193 @@
1
+ var trace = require('@fibery/helpers/utils/trace');
2
+ var _ = require('lodash');
3
+
4
+ function _interopDefaultLegacy (e) { return e && typeof e === 'object' && 'default' in e ? e : { 'default': e }; }
5
+
6
+ var ___default = /*#__PURE__*/_interopDefaultLegacy(_);
7
+
8
+ function _extends() {
9
+ _extends = Object.assign ? Object.assign.bind() : function (target) {
10
+ for (var i = 1; i < arguments.length; i++) {
11
+ var source = arguments[i];
12
+ for (var key in source) {
13
+ if (Object.prototype.hasOwnProperty.call(source, key)) {
14
+ target[key] = source[key];
15
+ }
16
+ }
17
+ }
18
+ return target;
19
+ };
20
+ return _extends.apply(this, arguments);
21
+ }
22
+
23
+ const stringify = x => {
24
+ if (x === undefined) {
25
+ return "undefined";
26
+ }
27
+ return JSON.stringify(x);
28
+ };
29
+ class NotImplementedError extends Error {
30
+ constructor(value, itemType = undefined) {
31
+ super([`"${stringify(value)}"`, itemType, "is not implemented"].filter(x => x !== undefined).join(" "));
32
+ }
33
+ }
34
+
35
+ const assertIsValidExpression = expression => {
36
+ trace.assert(Array.isArray(expression), "expression must be array", {
37
+ expression
38
+ });
39
+ trace.assert(expression.length > 0, "empty expression does not make any sense");
40
+ };
41
+ const dateRangeFunctions = new Set(["q/start", "q/end"]);
42
+ const firstLastFunctions = new Set(["q/first", "q/last"]);
43
+ const collectionOps = new Set(["q/count", "q/count-distinct", "q/sum", "q/min", "q/max", "q/avg", "q/join", "q/first", "q/last"]);
44
+ // [op, left, right]
45
+ // [=, $true, $false]
46
+ // [=, $my-id, ["fibery/id"]]
47
+ const binaryOperations = new Set(["=", "!=", "<", ">", "<=", ">=", "in",
48
+ //asc: obsolete,use q/in
49
+ "q/contains", "q/not-contains", "+", "-", "q/+", "q/-", "*", "/", "and", "or",
50
+ //asc: obsolete. use q/and, q/or
51
+ "q/and", "q/or", "q/in", "q/not-in"]);
52
+
53
+ // TODO: get rid of this. Use visitors everywhere
54
+ const naryOperations = new Set(["and", "or", "q/and", "q/or"]);
55
+ const logicalOperators = new Set(["and", "or", "q/and", "q/or"]);
56
+ const relationalOperators = new Set(["=", "!=", "<", ">", "<=", ">="]);
57
+ const mathOperators = new Set(["+", "-", "*", "/", "q/+", "q/-", "q/concat"]);
58
+ const isFunctionCallExpression = expression => expression.length > 1 && ___default["default"].isString(expression[0]) && (expression[0].startsWith("q/") || ["=", "!=", "<", ">", "<=", ">=", "+", "-", "*", "/", "in", "and", "or", "not-in"].includes(expression[0]));
59
+ const fromRootKeyword = "q/from-root";
60
+ const isFromRootFieldExpression = expression => ___default["default"].isArray(expression) && expression[0] === fromRootKeyword;
61
+ const isDateRangeFunctionExpression = expression => (expression.length === 2 || expression.length === 3) && dateRangeFunctions.has(expression[0]) && isFieldExpression(expression[1]);
62
+ const isCollectionFunctionExpression = expression =>
63
+ //expression has length 3 in case of q/join
64
+ (expression.length === 2 || expression.length === 3) && collectionOps.has(expression[0]);
65
+ const isAccessFunctionExpression = expresion => expresion.length === 2 && expresion[0] === "q/access?" && isFieldExpression(expresion[1]);
66
+ const isBinaryExpression = expression => expression.length === 3 && binaryOperations.has(expression[0]);
67
+ const isNaryExpression = expression => expression.length > 1 && naryOperations.has(expression[0]);
68
+ const isVariableExpression = expression => ___default["default"].isString(expression) && expression.startsWith("$");
69
+ const isFieldExpression = expression => Array.isArray(expression) && expression.every(x => !isVariableExpression(x) && !binaryOperations.has(x) && ___default["default"].isString(x));
70
+ const isQueryExpression = expression => {
71
+ if (___default["default"].isObject(expression) && "q/from" in expression) {
72
+ const fromExpression = expression["q/from"];
73
+ //asc: fromExpression === null for denormalizeSelect for reference collection case
74
+ return fromExpression === null || isFieldExpression(fromExpression);
75
+ }
76
+ return false;
77
+ };
78
+ // [q/count, [..., collection]]
79
+ // [q/start, [..., range]]
80
+ // [q/end, [..., range]]
81
+ // [q/access?, [..., field-expr]]
82
+ const isFunctionExpression = expression => {
83
+ if (!Array.isArray(expression)) {
84
+ return false;
85
+ }
86
+ if (isFieldExpression(expression)) {
87
+ return false;
88
+ } else if (isDateRangeFunctionExpression(expression) || isCollectionFunctionExpression(expression) || isAccessFunctionExpression(expression)) {
89
+ return true;
90
+ }
91
+ throw new Error("invalid expression:" + JSON.stringify(expression));
92
+ };
93
+ const collectFieldExpressions = (memo, expression) => {
94
+ if (isVariableExpression(expression)) ; else if (isFunctionCallExpression(expression)) {
95
+ for (const part of expression.slice(1)) {
96
+ if (isVariableExpression(part) || part === null) ; else {
97
+ if (___default["default"].isString(part)) {
98
+ // field path shortcut
99
+ memo.push([part]);
100
+ } else {
101
+ collectFieldExpressions(memo, part);
102
+ }
103
+ }
104
+ }
105
+ } else if (isFieldExpression(expression)) {
106
+ memo.push(expression);
107
+ } else if (expression["q/from"] && isFieldExpression(expression["q/from"])) {
108
+ const innerMemo = [];
109
+ expression["q/select"] && collectFieldExpressions(innerMemo, expression["q/select"]);
110
+ expression["q/where"] && collectFieldExpressions(innerMemo, expression["q/where"]);
111
+ for (const fieldExpression of innerMemo) {
112
+ memo.push([...expression["q/from"], ...fieldExpression]);
113
+ }
114
+ } else {
115
+ throw new NotImplementedError(expression, "expression");
116
+ }
117
+ };
118
+ const extractFieldExpressions = expression => {
119
+ const memo = [];
120
+ collectFieldExpressions(memo, expression);
121
+ return ___default["default"].uniqBy(memo, x => x.join(","));
122
+ };
123
+ const createExpressionVisitor = visitor => {
124
+ let visitorWithDefault = null;
125
+ const visitorDefault = {
126
+ visitVariableExpression: expression => expression,
127
+ visitFunctionCallExpression: ([fnName, ...args]) => [fnName, ...args.map(x => visitorWithDefault.visitExpression(x))],
128
+ visitFromRootFieldExpression: ([fromRootKeyword, ...rest]) => [fromRootKeyword, ...rest.map(x => visitorWithDefault.visitExpression(x))],
129
+ visitFieldExpression: expression => expression,
130
+ visitOrderByExpression: orderByExpression => orderByExpression.map(x => {
131
+ const [fieldExpression, orderDir] = x;
132
+ const fieldExpressionNew = visitorWithDefault.visitExpression(fieldExpression);
133
+ return [fieldExpressionNew, orderDir];
134
+ }),
135
+ visitQueryExpression: subQueryExpression => {
136
+ const {
137
+ "q/from": fromExpression,
138
+ "q/select": selectExpression,
139
+ "q/where": whereExpression,
140
+ "q/order-by": orderByExpression
141
+ } = subQueryExpression;
142
+ return _extends({}, subQueryExpression, fromExpression ? {
143
+ "q/from": visitorWithDefault.visitFieldExpression(fromExpression)
144
+ } : null, selectExpression ? {
145
+ "q/select": ___default["default"].isPlainObject(selectExpression) ? ___default["default"].mapValues(selectExpression, val => visitorWithDefault.visitExpression(val)) : visitorWithDefault.visitExpression(selectExpression)
146
+ } : null, whereExpression ? {
147
+ "q/where": visitorWithDefault.visitExpression(whereExpression)
148
+ } : null, orderByExpression ? {
149
+ "q/order-by": visitorWithDefault.visitOrderByExpression(orderByExpression)
150
+ } : null);
151
+ },
152
+ visitExpression: expression => {
153
+ if (expression === null) {
154
+ throw new NotImplementedError(expression, "expression");
155
+ } else if (isVariableExpression(expression)) {
156
+ return visitorWithDefault.visitVariableExpression(expression, visitorDefault);
157
+ } else if (isFromRootFieldExpression(expression)) {
158
+ return visitorWithDefault.visitFromRootFieldExpression(expression, visitorDefault);
159
+ } else if (isFunctionCallExpression(expression)) {
160
+ return visitorWithDefault.visitFunctionCallExpression(expression, visitorDefault);
161
+ } else if (isFieldExpression(expression)) {
162
+ return visitorWithDefault.visitFieldExpression(expression, visitorDefault);
163
+ } else if (isQueryExpression(expression)) {
164
+ return visitorWithDefault.visitQueryExpression(expression, visitorDefault);
165
+ } else {
166
+ throw new NotImplementedError(expression, "expression");
167
+ }
168
+ }
169
+ };
170
+ visitorWithDefault = _extends({}, visitorDefault, visitor);
171
+ return visitorWithDefault;
172
+ };
173
+
174
+ exports.assertIsValidExpression = assertIsValidExpression;
175
+ exports.createExpressionVisitor = createExpressionVisitor;
176
+ exports.dateRangeFunctions = dateRangeFunctions;
177
+ exports.extractFieldExpressions = extractFieldExpressions;
178
+ exports.firstLastFunctions = firstLastFunctions;
179
+ exports.fromRootKeyword = fromRootKeyword;
180
+ exports.isAccessFunctionExpression = isAccessFunctionExpression;
181
+ exports.isBinaryExpression = isBinaryExpression;
182
+ exports.isCollectionFunctionExpression = isCollectionFunctionExpression;
183
+ exports.isDateRangeFunctionExpression = isDateRangeFunctionExpression;
184
+ exports.isFieldExpression = isFieldExpression;
185
+ exports.isFromRootFieldExpression = isFromRootFieldExpression;
186
+ exports.isFunctionCallExpression = isFunctionCallExpression;
187
+ exports.isFunctionExpression = isFunctionExpression;
188
+ exports.isNaryExpression = isNaryExpression;
189
+ exports.isQueryExpression = isQueryExpression;
190
+ exports.isVariableExpression = isVariableExpression;
191
+ exports.logicalOperators = logicalOperators;
192
+ exports.mathOperators = mathOperators;
193
+ exports.relationalOperators = relationalOperators;