@budibase/shared-core 2.3.21-alpha.2 → 2.4.8-alpha.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/package.json +9 -5
- package/src/filters.ts +71 -45
- package/src/helpers.ts +23 -0
- package/src/index.ts +2 -0
- package/src/utils.ts +6 -0
- package/tsconfig.build.json +1 -1
- package/tsconfig.json +7 -2
package/package.json
CHANGED
|
@@ -1,19 +1,23 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@budibase/shared-core",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.4.8-alpha.1",
|
|
4
4
|
"description": "Shared data utils",
|
|
5
5
|
"main": "dist/src/index.js",
|
|
6
|
+
"module": "dist/src/index.js",
|
|
6
7
|
"types": "dist/src/index.d.ts",
|
|
7
8
|
"author": "Budibase",
|
|
8
9
|
"license": "GPL-3.0",
|
|
9
10
|
"scripts": {
|
|
10
|
-
"
|
|
11
|
-
"
|
|
11
|
+
"prebuild": "rimraf dist/",
|
|
12
|
+
"build": "tsc -p tsconfig.build.json",
|
|
13
|
+
"build:dev": "yarn prebuild && tsc --build --watch --preserveWatchOutput",
|
|
14
|
+
"dev:builder": "tsc -p tsconfig.build.json --watch"
|
|
12
15
|
},
|
|
13
16
|
"dependencies": {
|
|
14
|
-
"@budibase/types": "2.
|
|
17
|
+
"@budibase/types": "2.4.5-alpha.0"
|
|
15
18
|
},
|
|
16
19
|
"devDependencies": {
|
|
17
|
-
"typescript": "4.7.3"
|
|
20
|
+
"typescript": "4.7.3",
|
|
21
|
+
"rimraf": "3.0.2"
|
|
18
22
|
}
|
|
19
23
|
}
|
package/src/filters.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
|
-
import { SortDirection } from "@budibase/types"
|
|
1
|
+
import { Datasource, FieldType, SortDirection, SortType } from "@budibase/types"
|
|
2
2
|
import { OperatorOptions, SqlNumberTypeRangeMap } from "./constants"
|
|
3
|
+
import { deepGet } from "./helpers"
|
|
3
4
|
|
|
4
5
|
const HBS_REGEX = /{{([^{].*?)}}/g
|
|
5
6
|
|
|
@@ -8,9 +9,9 @@ const HBS_REGEX = /{{([^{].*?)}}/g
|
|
|
8
9
|
* @param type the data type
|
|
9
10
|
*/
|
|
10
11
|
export const getValidOperatorsForType = (
|
|
11
|
-
type:
|
|
12
|
+
type: FieldType,
|
|
12
13
|
field: string,
|
|
13
|
-
datasource: { tableId:
|
|
14
|
+
datasource: Datasource & { tableId: any } // TODO: is this table id ever populated?
|
|
14
15
|
) => {
|
|
15
16
|
const Op = OperatorOptions
|
|
16
17
|
const stringOps = [
|
|
@@ -31,7 +32,10 @@ export const getValidOperatorsForType = (
|
|
|
31
32
|
Op.NotEmpty,
|
|
32
33
|
Op.In,
|
|
33
34
|
]
|
|
34
|
-
let ops:
|
|
35
|
+
let ops: {
|
|
36
|
+
value: string
|
|
37
|
+
label: string
|
|
38
|
+
}[] = []
|
|
35
39
|
if (type === "string") {
|
|
36
40
|
ops = stringOps
|
|
37
41
|
} else if (type === "number") {
|
|
@@ -74,13 +78,13 @@ export const NoEmptyFilterStrings = [
|
|
|
74
78
|
OperatorOptions.NotEquals.value,
|
|
75
79
|
OperatorOptions.Contains.value,
|
|
76
80
|
OperatorOptions.NotContains.value,
|
|
77
|
-
]
|
|
81
|
+
] as (keyof QueryFields)[]
|
|
78
82
|
|
|
79
83
|
/**
|
|
80
84
|
* Removes any fields that contain empty strings that would cause inconsistent
|
|
81
85
|
* behaviour with how backend tables are filtered (no value means no filter).
|
|
82
86
|
*/
|
|
83
|
-
const cleanupQuery = (query:
|
|
87
|
+
const cleanupQuery = (query: Query) => {
|
|
84
88
|
if (!query) {
|
|
85
89
|
return query
|
|
86
90
|
}
|
|
@@ -88,9 +92,10 @@ const cleanupQuery = (query: { [x: string]: { [x: string]: any } }) => {
|
|
|
88
92
|
if (!query[filterField]) {
|
|
89
93
|
continue
|
|
90
94
|
}
|
|
91
|
-
|
|
95
|
+
|
|
96
|
+
for (let [key, value] of Object.entries(query[filterField]!)) {
|
|
92
97
|
if (value == null || value === "") {
|
|
93
|
-
delete query[filterField][key]
|
|
98
|
+
delete query[filterField]![key]
|
|
94
99
|
}
|
|
95
100
|
}
|
|
96
101
|
}
|
|
@@ -118,21 +123,52 @@ type Filter = {
|
|
|
118
123
|
externalType: keyof typeof SqlNumberTypeRangeMap
|
|
119
124
|
}
|
|
120
125
|
|
|
121
|
-
type Query =
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
126
|
+
type Query = QueryFields & QueryConfig
|
|
127
|
+
type QueryFields = {
|
|
128
|
+
string?: {
|
|
129
|
+
[key: string]: string
|
|
130
|
+
}
|
|
131
|
+
fuzzy?: {
|
|
132
|
+
[key: string]: string
|
|
133
|
+
}
|
|
134
|
+
range?: {
|
|
135
|
+
[key: string]: {
|
|
136
|
+
high: number | string
|
|
137
|
+
low: number | string
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
equal?: {
|
|
141
|
+
[key: string]: any
|
|
142
|
+
}
|
|
143
|
+
notEqual?: {
|
|
144
|
+
[key: string]: any
|
|
145
|
+
}
|
|
146
|
+
empty?: {
|
|
147
|
+
[key: string]: any
|
|
148
|
+
}
|
|
149
|
+
notEmpty?: {
|
|
150
|
+
[key: string]: any
|
|
151
|
+
}
|
|
152
|
+
oneOf?: {
|
|
153
|
+
[key: string]: any[]
|
|
154
|
+
}
|
|
155
|
+
contains?: {
|
|
156
|
+
[key: string]: any[]
|
|
157
|
+
}
|
|
158
|
+
notContains?: {
|
|
159
|
+
[key: string]: any[]
|
|
160
|
+
}
|
|
161
|
+
containsAny?: {
|
|
162
|
+
[key: string]: any[]
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
type QueryConfig = {
|
|
133
167
|
allOr?: boolean
|
|
134
168
|
}
|
|
135
169
|
|
|
170
|
+
type QueryFieldsType = keyof QueryFields
|
|
171
|
+
|
|
136
172
|
/**
|
|
137
173
|
* Builds a lucene JSON query from the filter structure generated in the builder
|
|
138
174
|
* @param filter the builder filter structure
|
|
@@ -194,7 +230,7 @@ export const buildLuceneQuery = (filter: Filter[]) => {
|
|
|
194
230
|
) {
|
|
195
231
|
value = value.split(",")
|
|
196
232
|
}
|
|
197
|
-
if (operator.startsWith("range")) {
|
|
233
|
+
if (operator.startsWith("range") && query.range) {
|
|
198
234
|
const minint =
|
|
199
235
|
SqlNumberTypeRangeMap[externalType]?.min || Number.MIN_SAFE_INTEGER
|
|
200
236
|
const maxint =
|
|
@@ -220,14 +256,18 @@ export const buildLuceneQuery = (filter: Filter[]) => {
|
|
|
220
256
|
// "equals false" needs to be "not equals true"
|
|
221
257
|
// "not equals false" needs to be "equals true"
|
|
222
258
|
if (operator === "equal" && value === false) {
|
|
259
|
+
query.notEqual = query.notEqual || {}
|
|
223
260
|
query.notEqual[field] = true
|
|
224
261
|
} else if (operator === "notEqual" && value === false) {
|
|
262
|
+
query.equal = query.equal || {}
|
|
225
263
|
query.equal[field] = true
|
|
226
264
|
} else {
|
|
227
|
-
query[operator][
|
|
265
|
+
query[operator] = query[operator] || {}
|
|
266
|
+
query[operator]![field] = value
|
|
228
267
|
}
|
|
229
268
|
} else {
|
|
230
|
-
query[operator][
|
|
269
|
+
query[operator] = query[operator] || {}
|
|
270
|
+
query[operator]![field] = value
|
|
231
271
|
}
|
|
232
272
|
}
|
|
233
273
|
})
|
|
@@ -235,29 +275,12 @@ export const buildLuceneQuery = (filter: Filter[]) => {
|
|
|
235
275
|
return query
|
|
236
276
|
}
|
|
237
277
|
|
|
238
|
-
const deepGet = (obj: { [x: string]: any }, key: string) => {
|
|
239
|
-
if (!obj || !key) {
|
|
240
|
-
return null
|
|
241
|
-
}
|
|
242
|
-
if (Object.prototype.hasOwnProperty.call(obj, key)) {
|
|
243
|
-
return obj[key]
|
|
244
|
-
}
|
|
245
|
-
const split = key.split(".")
|
|
246
|
-
for (let i = 0; i < split.length; i++) {
|
|
247
|
-
obj = obj?.[split[i]]
|
|
248
|
-
}
|
|
249
|
-
return obj
|
|
250
|
-
}
|
|
251
|
-
|
|
252
278
|
/**
|
|
253
279
|
* Performs a client-side lucene search on an array of data
|
|
254
280
|
* @param docs the data
|
|
255
281
|
* @param query the JSON lucene query
|
|
256
282
|
*/
|
|
257
|
-
export const runLuceneQuery = (
|
|
258
|
-
docs: any[],
|
|
259
|
-
query?: { [x: string]: any; sheet?: string }
|
|
260
|
-
) => {
|
|
283
|
+
export const runLuceneQuery = (docs: any[], query?: Query) => {
|
|
261
284
|
if (!docs || !Array.isArray(docs)) {
|
|
262
285
|
return []
|
|
263
286
|
}
|
|
@@ -270,7 +293,10 @@ export const runLuceneQuery = (
|
|
|
270
293
|
|
|
271
294
|
// Iterates over a set of filters and evaluates a fail function against a doc
|
|
272
295
|
const match =
|
|
273
|
-
(
|
|
296
|
+
(
|
|
297
|
+
type: QueryFieldsType,
|
|
298
|
+
failFn: (docValue: any, testValue: any) => boolean
|
|
299
|
+
) =>
|
|
274
300
|
(doc: any) => {
|
|
275
301
|
const filters = Object.entries(query![type] || {})
|
|
276
302
|
for (let i = 0; i < filters.length; i++) {
|
|
@@ -399,9 +425,9 @@ export const runLuceneQuery = (
|
|
|
399
425
|
*/
|
|
400
426
|
export const luceneSort = (
|
|
401
427
|
docs: any[],
|
|
402
|
-
sort: string
|
|
428
|
+
sort: string,
|
|
403
429
|
sortOrder: SortDirection,
|
|
404
|
-
sortType =
|
|
430
|
+
sortType = SortType.STRING
|
|
405
431
|
) => {
|
|
406
432
|
if (!sort || !sortOrder || !sortType) {
|
|
407
433
|
return docs
|
|
@@ -427,7 +453,7 @@ export const luceneSort = (
|
|
|
427
453
|
* @param docs the data
|
|
428
454
|
* @param limit the number of docs to limit to
|
|
429
455
|
*/
|
|
430
|
-
export const luceneLimit = (docs:
|
|
456
|
+
export const luceneLimit = (docs: any[], limit: string) => {
|
|
431
457
|
const numLimit = parseFloat(limit)
|
|
432
458
|
if (isNaN(numLimit)) {
|
|
433
459
|
return docs
|
package/src/helpers.ts
ADDED
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Gets a key within an object. The key supports dot syntax for retrieving deep
|
|
3
|
+
* fields - e.g. "a.b.c".
|
|
4
|
+
* Exact matches of keys with dots in them take precedence over nested keys of
|
|
5
|
+
* the same path - e.g. getting "a.b" from { "a.b": "foo", a: { b: "bar" } }
|
|
6
|
+
* will return "foo" over "bar".
|
|
7
|
+
* @param obj the object
|
|
8
|
+
* @param key the key
|
|
9
|
+
* @return {*|null} the value or null if a value was not found for this key
|
|
10
|
+
*/
|
|
11
|
+
export const deepGet = (obj: { [x: string]: any }, key: string) => {
|
|
12
|
+
if (!obj || !key) {
|
|
13
|
+
return null
|
|
14
|
+
}
|
|
15
|
+
if (Object.prototype.hasOwnProperty.call(obj, key)) {
|
|
16
|
+
return obj[key]
|
|
17
|
+
}
|
|
18
|
+
const split = key.split(".")
|
|
19
|
+
for (let i = 0; i < split.length; i++) {
|
|
20
|
+
obj = obj?.[split[i]]
|
|
21
|
+
}
|
|
22
|
+
return obj
|
|
23
|
+
}
|
package/src/index.ts
CHANGED
package/src/utils.ts
ADDED
package/tsconfig.build.json
CHANGED
package/tsconfig.json
CHANGED