@fibery/expression-utils 9.4.1 → 9.5.1

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.
package/lib/index.d.ts CHANGED
@@ -2,5 +2,21 @@ import * as paramsPlaceholders from "./src/params-placeholders";
2
2
  import * as utils from "./src/utils";
3
3
  import * as visitors from "./src/visitors";
4
4
  import * as contextVariables from "./src/context-variables";
5
- export { utils, visitors, paramsPlaceholders, contextVariables };
5
+ import * as operators from "./src/filter-expression/operators";
6
+ declare const filterExpression: {
7
+ findMatchedFilter: (filterExpression: import("./src/types").Expression, params: import("./src/tsfixme").$TSFixMe, operators: Array<import("./src/filter-expression/types").Operator>, schema: import("@fibery/schema").Schema | null, type: string) => {
8
+ operator: import("./src/filter-expression/types").Operator;
9
+ value: any;
10
+ expression: import("./src/types").Expression | undefined;
11
+ type: string;
12
+ id: string;
13
+ } | null;
14
+ operators: typeof operators;
15
+ convertToExpression: (pattern: any, filterExpression: any, value: any, getNextParamName: any) => {
16
+ expression: any;
17
+ params: {};
18
+ };
19
+ };
20
+ export { utils, visitors, paramsPlaceholders, contextVariables, filterExpression };
6
21
  export type { Expression, OrderBy, Query, Select, SubQuery, ExpressionVisitor, ExpressionVisitorWithDefault, } from "./src/types";
22
+ export type { Operator, ExpressionToFilterBy } from "./src/filter-expression/types";
package/lib/index.js CHANGED
@@ -33,7 +33,7 @@ var __importStar = (this && this.__importStar) || (function () {
33
33
  };
34
34
  })();
35
35
  Object.defineProperty(exports, "__esModule", { value: true });
36
- exports.contextVariables = exports.paramsPlaceholders = exports.visitors = exports.utils = void 0;
36
+ exports.filterExpression = exports.contextVariables = exports.paramsPlaceholders = exports.visitors = exports.utils = void 0;
37
37
  const paramsPlaceholders = __importStar(require("./src/params-placeholders"));
38
38
  exports.paramsPlaceholders = paramsPlaceholders;
39
39
  const utils = __importStar(require("./src/utils"));
@@ -42,3 +42,8 @@ const visitors = __importStar(require("./src/visitors"));
42
42
  exports.visitors = visitors;
43
43
  const contextVariables = __importStar(require("./src/context-variables"));
44
44
  exports.contextVariables = contextVariables;
45
+ const filter_expression_1 = require("./src/filter-expression");
46
+ const patternToExpression_1 = require("./src/filter-expression/patternToExpression");
47
+ const operators = __importStar(require("./src/filter-expression/operators"));
48
+ const filterExpression = { findMatchedFilter: filter_expression_1.findMatchedFilter, operators, convertToExpression: patternToExpression_1.convertToExpression };
49
+ exports.filterExpression = filterExpression;
@@ -0,0 +1,11 @@
1
+ import { Expression } from "../types";
2
+ import { Operator } from "./types";
3
+ import { Schema } from "@fibery/schema";
4
+ import { $TSFixMe } from "../tsfixme";
5
+ export declare const findMatchedFilter: (filterExpression: Expression, params: $TSFixMe, operators: Array<Operator>, schema: Schema | null, type: string) => {
6
+ operator: Operator;
7
+ value: any;
8
+ expression: Expression | undefined;
9
+ type: string;
10
+ id: string;
11
+ } | null;
@@ -0,0 +1,42 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.findMatchedFilter = void 0;
4
+ const pattern_match_1 = require("./pattern-match");
5
+ const uuid_1 = require("uuid");
6
+ const errors_1 = require("../errors");
7
+ const findMatchedFilter = (filterExpression, params, operators, schema, type) => {
8
+ if (!schema || !Object.hasOwn(schema.typeObjectsByName, type)) {
9
+ return null;
10
+ }
11
+ const typeObject = schema.typeObjectsByName[type];
12
+ for (const operator of operators) {
13
+ if (operator.customMatch) {
14
+ const { matchSuccess, value, expression } = operator.customMatch(filterExpression, params, typeObject);
15
+ if (matchSuccess) {
16
+ return { operator, value, expression, type, id: (0, uuid_1.v4)() };
17
+ }
18
+ }
19
+ else {
20
+ const { matchSuccess, matches } = (0, pattern_match_1.doPatternMatch)(filterExpression, params, operator.pattern, typeObject);
21
+ if (matchSuccess) {
22
+ const paramMatches = matches.filter((x) => x.matchType === ":expression-match-type/param");
23
+ const expressionMatches = matches.filter((x) => x.matchType === ":expression-match-type/expression");
24
+ if (paramMatches.length > 1) {
25
+ throw new errors_1.NotImplementedError(`Operator pattern with several param placeholders`);
26
+ }
27
+ if (expressionMatches.length > 1) {
28
+ throw new errors_1.NotImplementedError(`Operator pattern with several expression placeholders`);
29
+ }
30
+ return {
31
+ operator,
32
+ value: paramMatches[0] && paramMatches[0].value,
33
+ expression: expressionMatches[0].value,
34
+ type,
35
+ id: (0, uuid_1.v4)(),
36
+ };
37
+ }
38
+ }
39
+ }
40
+ return null;
41
+ };
42
+ exports.findMatchedFilter = findMatchedFilter;
@@ -0,0 +1,39 @@
1
+ export function isDateTime(dateString: any): boolean;
2
+ export function getCompareOperatorForDateTime(id: any, title: any, operator: any, compareByStartInterval: any): {
3
+ id: any;
4
+ title: any;
5
+ customMatch: (where: any, params: any, typeObject: any) => {
6
+ matchSuccess: true;
7
+ expression: any;
8
+ value: any;
9
+ } | {
10
+ matchSuccess: boolean;
11
+ expression?: undefined;
12
+ value?: undefined;
13
+ };
14
+ customConvertToWhereExpression: (expression: any, value: any, getNextParamName: any) => {
15
+ expression: any[];
16
+ params: {
17
+ [x: number]: any;
18
+ };
19
+ };
20
+ type: string;
21
+ };
22
+ export function getWithinOperatorCustomConvertFn(leftPartExcluded: any): (expression: any, value: any, getNextParamName: any) => {
23
+ expression: (string | any[])[];
24
+ params: {
25
+ [x: number]: any;
26
+ };
27
+ };
28
+ export function getWithinOperatorCustomMatchFn(fieldPlaceholder: any, leftPartExcluded: any): (where: any, params: any, typeObject: any) => {
29
+ matchSuccess: true;
30
+ expression: any;
31
+ value: {
32
+ withinRangeStart: any;
33
+ withinRangeEnd: any;
34
+ };
35
+ } | {
36
+ matchSuccess: boolean;
37
+ expression?: undefined;
38
+ value?: undefined;
39
+ };
@@ -0,0 +1,174 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.getWithinOperatorCustomMatchFn = exports.getWithinOperatorCustomConvertFn = exports.getCompareOperatorForDateTime = exports.isDateTime = void 0;
7
+ const lodash_1 = __importDefault(require("lodash"));
8
+ const params_placeholders_1 = require("../params-placeholders");
9
+ const pattern_match_1 = require("./pattern-match");
10
+ const patternMatchPlaceholders_1 = require("./patternMatchPlaceholders");
11
+ const isoDateTimeSample = "2018-05-30T07:57:45.190Z";
12
+ const isoDateSample = "2018-05-30";
13
+ // we're considering date as date-time here because of field-convert
14
+ // from date to date-time, it changes field type but not touches constants in query-params
15
+ // so to keep filters and color-coding working we treat date as date-time here
16
+ // and later moment.js parses date string as date-time
17
+ const isDateTime = (dateString) => dateString.length === isoDateTimeSample.length || dateString.length === isoDateSample.length;
18
+ exports.isDateTime = isDateTime;
19
+ const getCompareOperatorForDateTime = (id, title, operator, compareByStartInterval) => ({
20
+ id,
21
+ title,
22
+ customMatch: (where, params, typeObject) => {
23
+ const { matchSuccess, matches: [fieldMatch, paramMatch], } = (0, pattern_match_1.doPatternMatch)(where, params, [
24
+ operator,
25
+ patternMatchPlaceholders_1.dateTimeOrDateTimeRangeExpressionPlaceholder,
26
+ (0, patternMatchPlaceholders_1.getParamPlaceholder)((x) => {
27
+ return params_placeholders_1.paramsPlaceholdersLookup[x] || (lodash_1.default.isString(x) && (0, exports.isDateTime)(x)) || (0, params_placeholders_1.parseRelativeDatePlaceholder)(x);
28
+ }),
29
+ ], typeObject);
30
+ if (matchSuccess) {
31
+ const relativeDateInfo = (0, params_placeholders_1.parseRelativeDatePlaceholder)(paramMatch.value);
32
+ if (relativeDateInfo && relativeDateInfo.isStartOfInterval === compareByStartInterval) {
33
+ const value = (0, params_placeholders_1.toRelativeDatePlaceholder)({ ...relativeDateInfo, isStartOfInterval: undefined });
34
+ return { matchSuccess, expression: fieldMatch.value, value };
35
+ }
36
+ else if (params_placeholders_1.paramsPlaceholdersLookup[paramMatch.value]) {
37
+ const value = lodash_1.default.findKey(params_placeholders_1.dateToDateTimeIntervalLookup, (val) => compareByStartInterval ? val.start === paramMatch.value : val.end === paramMatch.value);
38
+ return { matchSuccess, expression: fieldMatch.value, value };
39
+ }
40
+ else {
41
+ return {
42
+ matchSuccess,
43
+ expression: fieldMatch.value,
44
+ value: paramMatch.value,
45
+ };
46
+ }
47
+ }
48
+ return { matchSuccess: false };
49
+ },
50
+ customConvertToWhereExpression: (expression, value, getNextParamName) => {
51
+ const dateTimePlaceholders = params_placeholders_1.dateToDateTimeIntervalLookup[value];
52
+ let relativeDateInfo = null;
53
+ if (dateTimePlaceholders) {
54
+ const { start, end } = dateTimePlaceholders;
55
+ const intervalParamName = compareByStartInterval ? start : end;
56
+ return {
57
+ expression: [operator, expression, intervalParamName],
58
+ params: { [intervalParamName]: intervalParamName },
59
+ };
60
+ }
61
+ else if ((relativeDateInfo = (0, params_placeholders_1.parseRelativeDatePlaceholder)(value))) {
62
+ const paramName = (0, params_placeholders_1.toRelativeDatePlaceholder)({
63
+ ...relativeDateInfo,
64
+ isStartOfInterval: compareByStartInterval,
65
+ unitForStart: "day",
66
+ });
67
+ return {
68
+ expression: [operator, expression, paramName],
69
+ params: { [paramName]: paramName },
70
+ };
71
+ }
72
+ else {
73
+ const dateParamName = getNextParamName();
74
+ return {
75
+ expression: [operator, expression, dateParamName],
76
+ params: { [dateParamName]: value },
77
+ };
78
+ }
79
+ },
80
+ type: ":operator-type/binary",
81
+ });
82
+ exports.getCompareOperatorForDateTime = getCompareOperatorForDateTime;
83
+ const getWithinRangeParam = ({ withinRangeValue, getNextParamName }) => {
84
+ // check that it's a relative date.
85
+ const withinRangeDateInfo = (0, params_placeholders_1.parseRelativeDatePlaceholder)(withinRangeValue);
86
+ if (withinRangeValue && withinRangeDateInfo) {
87
+ return { key: withinRangeValue, value: withinRangeValue };
88
+ }
89
+ else {
90
+ return { key: getNextParamName(), value: withinRangeValue };
91
+ }
92
+ };
93
+ const getWithinOperatorCustomConvertFn = (leftPartExcluded) => {
94
+ return (expression, value, getNextParamName) => {
95
+ const { key: startParamKey, value: startParamValue } = getWithinRangeParam({
96
+ withinRangeValue: value.withinRangeStart,
97
+ getNextParamName,
98
+ compareByStartInterval: true,
99
+ });
100
+ const { key: endParamKey, value: endParamValue } = getWithinRangeParam({
101
+ withinRangeValue: value.withinRangeEnd,
102
+ getNextParamName,
103
+ compareByStartInterval: false,
104
+ });
105
+ return {
106
+ // See https://the.fibery.io/SoftDev/bug/The-left-part-of-'is-within'-condition-is-not-included-into-filter-12264
107
+ expression: ["and", [leftPartExcluded ? ">" : ">=", expression, startParamKey], ["<=", expression, endParamKey]],
108
+ params: {
109
+ [startParamKey]: startParamValue,
110
+ [endParamKey]: endParamValue,
111
+ },
112
+ };
113
+ };
114
+ };
115
+ exports.getWithinOperatorCustomConvertFn = getWithinOperatorCustomConvertFn;
116
+ const oldWithinPlaceholderToRelativeDateLookup = {
117
+ // we don't need $start-of-day at all for date. We can omit it, but it won't hurt in general.
118
+ [params_placeholders_1.weekAgoDateParamPlaceholder]: "$start-of-day-1-week-before-now-date",
119
+ [params_placeholders_1.monthAgoDateParamPlaceholder]: "$start-of-day-1-month-before-now-date",
120
+ [params_placeholders_1.yearAgoDateParamPlaceholder]: "$start-of-day-1-year-before-now-date",
121
+ [params_placeholders_1.weekFromNowDateParamPlaceholder]: "$start-of-day-1-week-after-now-date",
122
+ [params_placeholders_1.monthFromNowDateParamPlaceholder]: "$start-of-day-1-month-after-now-date",
123
+ [params_placeholders_1.yearFromNowDateParamPlaceholder]: "$start-of-day-1-year-after-now-date",
124
+ [params_placeholders_1.todayDateParamPlaceholder]: "$end-of-day-0-day-before-now-date",
125
+ [params_placeholders_1.weekAgoStartDateTimeParamPlaceholder]: "$start-of-day-1-week-before-now-date-time",
126
+ [params_placeholders_1.monthAgoStartDateTimeParamPlaceholder]: "$start-of-day-1-month-before-now-date-time",
127
+ [params_placeholders_1.yearAgoStartDateTimeParamPlaceholder]: "$start-of-day-1-year-before-now-date-time",
128
+ [params_placeholders_1.weekFromNowEndDateTimeParamPlaceholder]: "$start-of-day-1-week-after-now-date-time",
129
+ [params_placeholders_1.monthFromNowEndDateTimeParamPlaceholder]: "$start-of-day-1-month-after-now-date-time",
130
+ [params_placeholders_1.yearFromNowEndDateTimeParamPlaceholder]: "$start-of-day-1-year-after-now-date-time",
131
+ [params_placeholders_1.todayEndDateTimeParamPlaceholder]: "$end-of-day-0-day-before-now-date-time",
132
+ };
133
+ const getWithinRangeValue = (paramMatchValue) => {
134
+ if (Object.hasOwn(oldWithinPlaceholderToRelativeDateLookup, paramMatchValue)) {
135
+ return oldWithinPlaceholderToRelativeDateLookup[paramMatchValue];
136
+ }
137
+ else {
138
+ return paramMatchValue;
139
+ }
140
+ };
141
+ const matchWithinOperator = (pattern, where, params, typeObject) => {
142
+ const { matchSuccess, matches: [firstFieldMatch, firstParamMatch, secondFieldMatch, secondParamMatch], } = (0, pattern_match_1.doPatternMatch)(where, params, pattern, typeObject);
143
+ if (matchSuccess && lodash_1.default.isEqual(firstFieldMatch.value, secondFieldMatch.value)) {
144
+ return {
145
+ matchSuccess,
146
+ expression: firstFieldMatch.value,
147
+ value: {
148
+ withinRangeStart: getWithinRangeValue(firstParamMatch.value),
149
+ withinRangeEnd: getWithinRangeValue(secondParamMatch.value),
150
+ },
151
+ };
152
+ }
153
+ return { matchSuccess: false };
154
+ };
155
+ const getWithinOperatorCustomMatchFn = (fieldPlaceholder, leftPartExcluded) => {
156
+ return (where, params, typeObject) => {
157
+ const match = matchWithinOperator([
158
+ "and",
159
+ [">", fieldPlaceholder, (0, patternMatchPlaceholders_1.getParamPlaceholder)((x) => x !== null)],
160
+ ["<=", fieldPlaceholder, (0, patternMatchPlaceholders_1.getParamPlaceholder)((x) => x !== null)],
161
+ ], where, params, typeObject);
162
+ if (!match.matchSuccess && !leftPartExcluded) {
163
+ // see https://the.fibery.io/SoftDev/bug/The-left-part-of-'is-within'-condition-is-not-included-into-filter-12264
164
+ // we probably want to have the same for date-time, but then we will intersect with the "match" function of "operatorDateTimeIs".
165
+ return matchWithinOperator([
166
+ "and",
167
+ [">=", fieldPlaceholder, (0, patternMatchPlaceholders_1.getParamPlaceholder)((x) => x !== null)],
168
+ ["<=", fieldPlaceholder, (0, patternMatchPlaceholders_1.getParamPlaceholder)((x) => x !== null)],
169
+ ], where, params, typeObject);
170
+ }
171
+ return match;
172
+ };
173
+ };
174
+ exports.getWithinOperatorCustomMatchFn = getWithinOperatorCustomMatchFn;