feathers-utils 6.0.0 → 7.0.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/README.md +3 -4
- package/dist/index.cjs +81 -99
- package/dist/index.d.cts +35 -33
- package/dist/index.d.mts +35 -33
- package/dist/index.d.ts +35 -33
- package/dist/index.mjs +81 -99
- package/package.json +29 -32
- package/src/.DS_Store +0 -0
- package/src/filters/array.ts +11 -13
- package/src/filters/index.ts +2 -2
- package/src/filters/object.ts +11 -11
- package/src/hooks/.DS_Store +0 -0
- package/src/hooks/checkMulti.ts +98 -82
- package/src/hooks/createRelated.ts +41 -41
- package/src/hooks/forEach.ts +32 -32
- package/src/hooks/from-client-for-server/common.ts +1 -1
- package/src/hooks/from-client-for-server/index.ts +2 -2
- package/src/hooks/from-client-for-server/paramsForServer.ts +32 -32
- package/src/hooks/from-client-for-server/paramsFromClient.ts +25 -25
- package/src/hooks/index.ts +9 -9
- package/src/hooks/onDelete.ts +54 -55
- package/src/hooks/parseFields.ts +13 -13
- package/src/hooks/removeRelated.ts +22 -20
- package/src/hooks/runPerItem.ts +17 -18
- package/src/hooks/setData.ts +295 -264
- package/src/index.ts +6 -6
- package/src/mixins/debounce-mixin/DebouncedStore.ts +29 -29
- package/src/mixins/debounce-mixin/debounceMixin.ts +17 -17
- package/src/mixins/debounce-mixin/index.ts +3 -3
- package/src/mixins/debounce-mixin/types.ts +9 -9
- package/src/mixins/debounce-mixin/utils.ts +3 -3
- package/src/mixins/index.ts +1 -1
- package/src/types.ts +3 -5
- package/src/typesInternal.ts +14 -14
- package/src/utility-types/index.ts +48 -48
- package/src/utils/_utils.internal.ts +5 -5
- package/src/utils/defineHooks.ts +8 -8
- package/src/utils/deflattenQuery.ts +31 -31
- package/src/utils/filterQuery.ts +58 -58
- package/src/utils/flattenQuery.ts +54 -54
- package/src/utils/getItemsIsArray.ts +148 -149
- package/src/utils/getPaginate.ts +31 -31
- package/src/utils/index.ts +17 -17
- package/src/utils/isMulti.ts +48 -40
- package/src/utils/isPaginated.ts +30 -30
- package/src/utils/markHookForSkip.ts +177 -178
- package/src/utils/mergeQuery/index.ts +3 -3
- package/src/utils/mergeQuery/mergeArrays.ts +67 -67
- package/src/utils/mergeQuery/mergeQuery.ts +211 -211
- package/src/utils/mergeQuery/types.ts +12 -12
- package/src/utils/mergeQuery/utils.ts +224 -224
- package/src/utils/optimizeBatchPatch.ts +42 -42
- package/src/utils/pushSet.ts +57 -57
- package/src/utils/setQueryKeySafely.ts +68 -68
- package/src/utils/setResultEmpty.ts +125 -123
- package/src/utils/shouldSkip.ts +72 -72
- package/src/utils/toJSON.ts +4 -4
- package/src/utils/validateQueryProperty.ts +10 -10
- package/src/hooks/makeSequelizeQuery.ts_ +0 -90
|
@@ -1,15 +1,15 @@
|
|
|
1
|
-
import _merge from
|
|
2
|
-
import type { Query } from
|
|
1
|
+
import _merge from 'lodash/merge.js'
|
|
2
|
+
import type { Query } from '@feathersjs/feathers'
|
|
3
3
|
import {
|
|
4
4
|
areQueriesOverlapping,
|
|
5
5
|
handleArray,
|
|
6
6
|
handleCircular,
|
|
7
7
|
isQueryMoreExplicitThanQuery,
|
|
8
8
|
makeDefaultOptions,
|
|
9
|
-
} from
|
|
10
|
-
import type { MergeQueryOptions } from
|
|
11
|
-
import { filterQuery } from
|
|
12
|
-
import { hasOwnProperty, isEmpty } from
|
|
9
|
+
} from './utils.js'
|
|
10
|
+
import type { MergeQueryOptions } from './types.js'
|
|
11
|
+
import { filterQuery } from '../filterQuery.js'
|
|
12
|
+
import { hasOwnProperty, isEmpty } from '../_utils.internal.js'
|
|
13
13
|
|
|
14
14
|
/**
|
|
15
15
|
* Merges two queries into one.
|
|
@@ -23,111 +23,111 @@ export function mergeQuery(
|
|
|
23
23
|
source: Query,
|
|
24
24
|
_options?: Partial<MergeQueryOptions>,
|
|
25
25
|
): Query {
|
|
26
|
-
const options = makeDefaultOptions(_options)
|
|
26
|
+
const options = makeDefaultOptions(_options)
|
|
27
27
|
|
|
28
|
-
const { query: targetQuery, ...targetFilters } = filterQuery(target)
|
|
28
|
+
const { query: targetQuery, ...targetFilters } = filterQuery(target)
|
|
29
29
|
|
|
30
30
|
// eslint-disable-next-line prefer-const
|
|
31
|
-
let { query: sourceQuery, ...sourceFilters } = filterQuery(source)
|
|
31
|
+
let { query: sourceQuery, ...sourceFilters } = filterQuery(source)
|
|
32
32
|
|
|
33
33
|
//#region filters
|
|
34
|
-
handleArray(targetFilters, sourceFilters, [
|
|
34
|
+
handleArray(targetFilters, sourceFilters, ['$select'], options)
|
|
35
35
|
// remaining filters
|
|
36
|
-
delete sourceFilters[
|
|
37
|
-
_merge(targetFilters, sourceFilters)
|
|
36
|
+
delete sourceFilters['$select']
|
|
37
|
+
_merge(targetFilters, sourceFilters)
|
|
38
38
|
|
|
39
39
|
//#endregion
|
|
40
40
|
|
|
41
|
-
const subsetQuery = isQueryMoreExplicitThanQuery(targetQuery, sourceQuery)
|
|
41
|
+
const subsetQuery = isQueryMoreExplicitThanQuery(targetQuery, sourceQuery)
|
|
42
42
|
|
|
43
43
|
if (
|
|
44
|
-
options.defaultHandle ===
|
|
44
|
+
options.defaultHandle === 'intersect' &&
|
|
45
45
|
!!subsetQuery &&
|
|
46
|
-
!hasOwnProperty(targetQuery,
|
|
47
|
-
!hasOwnProperty(sourceQuery,
|
|
46
|
+
!hasOwnProperty(targetQuery, '$or', '$and') &&
|
|
47
|
+
!hasOwnProperty(sourceQuery, '$or', '$and')
|
|
48
48
|
) {
|
|
49
49
|
return {
|
|
50
50
|
...targetFilters,
|
|
51
51
|
...subsetQuery,
|
|
52
|
-
}
|
|
52
|
+
}
|
|
53
53
|
}
|
|
54
54
|
|
|
55
55
|
if (
|
|
56
|
-
options.defaultHandle ===
|
|
56
|
+
options.defaultHandle === 'intersect' &&
|
|
57
57
|
!areQueriesOverlapping(targetQuery, sourceQuery) &&
|
|
58
|
-
!hasOwnProperty(targetQuery,
|
|
59
|
-
!hasOwnProperty(sourceQuery,
|
|
58
|
+
!hasOwnProperty(targetQuery, '$or', '$and') &&
|
|
59
|
+
!hasOwnProperty(sourceQuery, '$or', '$and')
|
|
60
60
|
) {
|
|
61
61
|
return {
|
|
62
62
|
...targetFilters,
|
|
63
63
|
...targetQuery,
|
|
64
64
|
...sourceQuery,
|
|
65
|
-
}
|
|
65
|
+
}
|
|
66
66
|
}
|
|
67
67
|
|
|
68
68
|
//#region '$or' / '$and'
|
|
69
69
|
|
|
70
70
|
if (
|
|
71
71
|
options.useLogicalConjunction &&
|
|
72
|
-
(options.defaultHandle ===
|
|
73
|
-
options.defaultHandle ===
|
|
72
|
+
(options.defaultHandle === 'combine' ||
|
|
73
|
+
options.defaultHandle === 'intersect') &&
|
|
74
74
|
!isEmpty(targetQuery)
|
|
75
75
|
) {
|
|
76
|
-
const logicalOp = options.defaultHandle ===
|
|
76
|
+
const logicalOp = options.defaultHandle === 'combine' ? '$or' : '$and'
|
|
77
77
|
if (hasOwnProperty(sourceQuery, logicalOp)) {
|
|
78
78
|
// omit '$or'/'$and' and put all other props into '$or'/'$and'
|
|
79
|
-
const andOr = sourceQuery[logicalOp] as unknown[]
|
|
80
|
-
delete sourceQuery[logicalOp]
|
|
81
|
-
andOr.push(sourceQuery)
|
|
82
|
-
sourceQuery = { [logicalOp]: andOr }
|
|
79
|
+
const andOr = sourceQuery[logicalOp] as unknown[]
|
|
80
|
+
delete sourceQuery[logicalOp]
|
|
81
|
+
andOr.push(sourceQuery)
|
|
82
|
+
sourceQuery = { [logicalOp]: andOr }
|
|
83
83
|
} else {
|
|
84
|
-
sourceQuery = { [logicalOp]: [sourceQuery] }
|
|
84
|
+
sourceQuery = { [logicalOp]: [sourceQuery] }
|
|
85
85
|
}
|
|
86
86
|
}
|
|
87
87
|
|
|
88
88
|
//#endregion
|
|
89
89
|
|
|
90
|
-
const keys = Object.keys(sourceQuery)
|
|
90
|
+
const keys = Object.keys(sourceQuery)
|
|
91
91
|
for (let i = 0, n = keys.length; i < n; i++) {
|
|
92
|
-
const key = keys[i]
|
|
93
|
-
handleCircular(targetQuery, sourceQuery, [key], options)
|
|
92
|
+
const key = keys[i]
|
|
93
|
+
handleCircular(targetQuery, sourceQuery, [key], options)
|
|
94
94
|
}
|
|
95
95
|
|
|
96
96
|
return {
|
|
97
97
|
...targetFilters,
|
|
98
98
|
...targetQuery,
|
|
99
|
-
}
|
|
99
|
+
}
|
|
100
100
|
}
|
|
101
101
|
|
|
102
102
|
if (import.meta.vitest) {
|
|
103
|
-
const { it, describe, expect } = import.meta.vitest
|
|
104
|
-
const { feathers } = await import(
|
|
105
|
-
const { MemoryService } = await import(
|
|
106
|
-
const { filterArray } = await import(
|
|
107
|
-
const { Forbidden } = await import(
|
|
103
|
+
const { it, describe, expect } = import.meta.vitest
|
|
104
|
+
const { feathers } = await import('@feathersjs/feathers')
|
|
105
|
+
const { MemoryService } = await import('@feathersjs/memory')
|
|
106
|
+
const { filterArray } = await import('../../filters/array.js')
|
|
107
|
+
const { Forbidden } = await import('@feathersjs/errors')
|
|
108
108
|
|
|
109
|
-
describe(
|
|
110
|
-
it(
|
|
109
|
+
describe('general', function () {
|
|
110
|
+
it('$limit: -1', function () {
|
|
111
111
|
expect(mergeQuery({ $limit: -1 }, { id: 1 })).toEqual({
|
|
112
112
|
$limit: -1,
|
|
113
113
|
id: 1,
|
|
114
|
-
})
|
|
115
|
-
})
|
|
116
|
-
})
|
|
114
|
+
})
|
|
115
|
+
})
|
|
116
|
+
})
|
|
117
117
|
|
|
118
|
-
describe(
|
|
119
|
-
const app = feathers()
|
|
118
|
+
describe('simple objects passing', function () {
|
|
119
|
+
const app = feathers()
|
|
120
120
|
app.use(
|
|
121
|
-
|
|
121
|
+
'/service',
|
|
122
122
|
new MemoryService({
|
|
123
123
|
paginate: { default: 10, max: 100 },
|
|
124
|
-
operators: [
|
|
124
|
+
operators: ['$and'],
|
|
125
125
|
filters: {
|
|
126
|
-
...filterArray(
|
|
126
|
+
...filterArray('$and'),
|
|
127
127
|
},
|
|
128
128
|
}),
|
|
129
|
-
)
|
|
130
|
-
const service = app.service(
|
|
129
|
+
)
|
|
130
|
+
const service = app.service('/service')
|
|
131
131
|
const passingPairs = {
|
|
132
132
|
empty: {
|
|
133
133
|
target: {},
|
|
@@ -138,100 +138,100 @@ if (import.meta.vitest) {
|
|
|
138
138
|
target: {
|
|
139
139
|
target: { id: 1, test1: true },
|
|
140
140
|
source: { id: 2, test2: false },
|
|
141
|
-
options: { defaultHandle:
|
|
141
|
+
options: { defaultHandle: 'target', useLogicalConjunction: false },
|
|
142
142
|
expected: { id: 1, test1: true, test2: false },
|
|
143
143
|
},
|
|
144
144
|
source: {
|
|
145
145
|
target: { id: 1, test1: true },
|
|
146
146
|
source: { id: 2, test2: false },
|
|
147
|
-
options: { defaultHandle:
|
|
147
|
+
options: { defaultHandle: 'source', useLogicalConjunction: false },
|
|
148
148
|
expected: { id: 2, test1: true, test2: false },
|
|
149
149
|
},
|
|
150
|
-
|
|
150
|
+
'native booleans': {
|
|
151
151
|
target: { test: true },
|
|
152
152
|
source: { test: false },
|
|
153
153
|
options: { useLogicalConjunction: false },
|
|
154
154
|
expected: { test: false },
|
|
155
155
|
},
|
|
156
|
-
|
|
156
|
+
'native nested to boolean': {
|
|
157
157
|
target: { test: { nested: [{ deep: true }] } },
|
|
158
158
|
source: { test: false },
|
|
159
159
|
options: { useLogicalConjunction: false },
|
|
160
160
|
expected: { test: false },
|
|
161
161
|
},
|
|
162
|
-
}
|
|
162
|
+
}
|
|
163
163
|
for (const key in passingPairs) {
|
|
164
|
-
const { target, source, options, expected } = passingPairs[key]
|
|
164
|
+
const { target, source, options, expected } = (passingPairs as any)[key]
|
|
165
165
|
it(`'${key}'`, function () {
|
|
166
166
|
expect(
|
|
167
167
|
mergeQuery(target, source, Object.assign({ service }, options)),
|
|
168
|
-
).toEqual(expected)
|
|
169
|
-
})
|
|
168
|
+
).toEqual(expected)
|
|
169
|
+
})
|
|
170
170
|
}
|
|
171
|
-
})
|
|
171
|
+
})
|
|
172
172
|
|
|
173
|
-
describe(
|
|
174
|
-
const app = feathers()
|
|
173
|
+
describe('simple objects passing with handle combine', function () {
|
|
174
|
+
const app = feathers()
|
|
175
175
|
app.use(
|
|
176
|
-
|
|
176
|
+
'/service',
|
|
177
177
|
new MemoryService({
|
|
178
178
|
paginate: { default: 10, max: 100 },
|
|
179
|
-
operators: [
|
|
179
|
+
operators: ['$and'],
|
|
180
180
|
filters: {
|
|
181
|
-
...filterArray(
|
|
181
|
+
...filterArray('$and'),
|
|
182
182
|
},
|
|
183
183
|
}),
|
|
184
|
-
)
|
|
185
|
-
const service = app.service(
|
|
184
|
+
)
|
|
185
|
+
const service = app.service('/service')
|
|
186
186
|
const passingPairs = {
|
|
187
|
-
|
|
187
|
+
'combine two numbers': {
|
|
188
188
|
target: { id: 1 },
|
|
189
189
|
source: { id: 2 },
|
|
190
|
-
options: { defaultHandle:
|
|
190
|
+
options: { defaultHandle: 'combine', useLogicalConjunction: false },
|
|
191
191
|
expected: { id: { $in: [1, 2] } },
|
|
192
192
|
},
|
|
193
|
-
|
|
194
|
-
target: { id:
|
|
193
|
+
'combine $in different types': {
|
|
194
|
+
target: { id: '1' },
|
|
195
195
|
source: { id: 2 },
|
|
196
|
-
options: { defaultHandle:
|
|
197
|
-
expected: { id: { $in: [
|
|
196
|
+
options: { defaultHandle: 'combine', useLogicalConjunction: false },
|
|
197
|
+
expected: { id: { $in: ['1', 2] } },
|
|
198
198
|
},
|
|
199
|
-
|
|
199
|
+
'combine number to $in with overlapping': {
|
|
200
200
|
target: { id: 1 },
|
|
201
201
|
source: { id: { $in: [1, 2] } },
|
|
202
|
-
options: { defaultHandle:
|
|
202
|
+
options: { defaultHandle: 'combine', useLogicalConjunction: false },
|
|
203
203
|
expected: { id: { $in: [1, 2] } },
|
|
204
204
|
},
|
|
205
|
-
|
|
205
|
+
'combine numbers in $in': {
|
|
206
206
|
target: { id: 1 },
|
|
207
207
|
source: { id: { $in: [2, 3] } },
|
|
208
|
-
options: { defaultHandle:
|
|
208
|
+
options: { defaultHandle: 'combine', useLogicalConjunction: false },
|
|
209
209
|
expected: { id: { $in: [2, 3, 1] } },
|
|
210
210
|
},
|
|
211
|
-
|
|
211
|
+
'combine two $in': {
|
|
212
212
|
target: { id: { $in: [2] } },
|
|
213
213
|
source: { id: { $in: [3, 4] } },
|
|
214
|
-
options: { defaultHandle:
|
|
214
|
+
options: { defaultHandle: 'combine', useLogicalConjunction: false },
|
|
215
215
|
expected: { id: { $in: [2, 3, 4] } },
|
|
216
216
|
},
|
|
217
|
-
|
|
217
|
+
'combine two $or queries': {
|
|
218
218
|
target: { $or: [{ id: 1 }, { id: 2 }] },
|
|
219
219
|
source: { $or: [{ id: 3 }] },
|
|
220
|
-
options: { defaultHandle:
|
|
220
|
+
options: { defaultHandle: 'combine', useLogicalConjunction: false },
|
|
221
221
|
expected: { $or: [{ id: 1 }, { id: 2 }, { id: 3 }] },
|
|
222
222
|
},
|
|
223
|
-
|
|
223
|
+
'combine two $and queries': {
|
|
224
224
|
target: { $and: [{ id: 1 }, { id: 2 }] },
|
|
225
225
|
source: { $and: [{ id: 3 }] },
|
|
226
|
-
options: { defaultHandle:
|
|
226
|
+
options: { defaultHandle: 'combine', useLogicalConjunction: false },
|
|
227
227
|
expected: {
|
|
228
228
|
$or: [{ $and: [{ id: 1 }, { id: 2 }] }, { $and: [{ id: 3 }] }],
|
|
229
229
|
},
|
|
230
230
|
},
|
|
231
|
-
|
|
231
|
+
'combine $or and $and queries': {
|
|
232
232
|
target: { $or: [{ id: 1 }, { id: 2 }], $and: [{ id: 4 }] },
|
|
233
233
|
source: { $or: [{ id: 3 }], $and: [{ id: 5 }] },
|
|
234
|
-
options: { defaultHandle:
|
|
234
|
+
options: { defaultHandle: 'combine', useLogicalConjunction: false },
|
|
235
235
|
expected: {
|
|
236
236
|
$or: [
|
|
237
237
|
{ id: 1 },
|
|
@@ -242,161 +242,161 @@ if (import.meta.vitest) {
|
|
|
242
242
|
],
|
|
243
243
|
},
|
|
244
244
|
},
|
|
245
|
-
|
|
245
|
+
'removes empty $or for both': {
|
|
246
246
|
target: { $or: [{}] },
|
|
247
247
|
source: { $or: [{}] },
|
|
248
|
-
options: { defaultHandle:
|
|
248
|
+
options: { defaultHandle: 'combine', useLogicalConjunction: false },
|
|
249
249
|
expected: {},
|
|
250
250
|
},
|
|
251
|
-
|
|
251
|
+
'removes empty $and for both': {
|
|
252
252
|
target: { $and: [{}] },
|
|
253
253
|
source: { $and: [{}] },
|
|
254
|
-
options: { defaultHandle:
|
|
254
|
+
options: { defaultHandle: 'combine', useLogicalConjunction: false },
|
|
255
255
|
expected: {},
|
|
256
256
|
},
|
|
257
|
-
|
|
257
|
+
'removes duplicate entries in $or': {
|
|
258
258
|
target: { $or: [{ id: 1 }, { id: 1 }, { id: 2 }] },
|
|
259
259
|
source: { $or: [{ id: 2 }] },
|
|
260
|
-
options: { defaultHandle:
|
|
260
|
+
options: { defaultHandle: 'combine', useLogicalConjunction: false },
|
|
261
261
|
expected: { $or: [{ id: 1 }, { id: 2 }] },
|
|
262
262
|
},
|
|
263
|
-
|
|
264
|
-
target: { id: { $in: [1, 2, 3] }, status:
|
|
263
|
+
'$in and other props': {
|
|
264
|
+
target: { id: { $in: [1, 2, 3] }, status: 'complete' },
|
|
265
265
|
source: {
|
|
266
266
|
id: { $in: [2, 3, 4] },
|
|
267
|
-
status: { $in: [
|
|
267
|
+
status: { $in: ['complete', 'pending', 'draft'] },
|
|
268
268
|
},
|
|
269
|
-
options: { defaultHandle:
|
|
270
|
-
expected: { id: { $in: [2, 3] }, status:
|
|
269
|
+
options: { defaultHandle: 'intersect', useLogicalConjunction: false },
|
|
270
|
+
expected: { id: { $in: [2, 3] }, status: 'complete' },
|
|
271
271
|
},
|
|
272
|
-
}
|
|
272
|
+
}
|
|
273
273
|
for (const key in passingPairs) {
|
|
274
|
-
const { target, source, options, expected } = passingPairs[key]
|
|
274
|
+
const { target, source, options, expected } = (passingPairs as any)[key]
|
|
275
275
|
it(`'${key}'`, function () {
|
|
276
276
|
expect(
|
|
277
277
|
mergeQuery(target, source, Object.assign({ service }, options)),
|
|
278
|
-
).toEqual(expected)
|
|
279
|
-
})
|
|
278
|
+
).toEqual(expected)
|
|
279
|
+
})
|
|
280
280
|
}
|
|
281
281
|
|
|
282
|
-
describe(
|
|
283
|
-
const app = feathers()
|
|
282
|
+
describe('with useLogicalConjunction: true', function () {
|
|
283
|
+
const app = feathers()
|
|
284
284
|
app.use(
|
|
285
|
-
|
|
285
|
+
'/service',
|
|
286
286
|
new MemoryService({
|
|
287
287
|
paginate: { default: 10, max: 100 },
|
|
288
|
-
operators: [
|
|
288
|
+
operators: ['$and'],
|
|
289
289
|
filters: {
|
|
290
|
-
...filterArray(
|
|
290
|
+
...filterArray('$and'),
|
|
291
291
|
},
|
|
292
292
|
}),
|
|
293
|
-
)
|
|
294
|
-
const service = app.service(
|
|
293
|
+
)
|
|
294
|
+
const service = app.service('/service')
|
|
295
295
|
const passingPairs = {
|
|
296
|
-
|
|
296
|
+
'merge empty source': {
|
|
297
297
|
target: { id: 1 },
|
|
298
298
|
source: {},
|
|
299
|
-
options: { defaultHandle:
|
|
299
|
+
options: { defaultHandle: 'combine', useLogicalConjunction: true },
|
|
300
300
|
expected: { id: 1 },
|
|
301
301
|
},
|
|
302
|
-
|
|
302
|
+
'merge empty target': {
|
|
303
303
|
target: {},
|
|
304
304
|
source: { id: 1 },
|
|
305
|
-
options: { defaultHandle:
|
|
305
|
+
options: { defaultHandle: 'combine', useLogicalConjunction: true },
|
|
306
306
|
expected: { id: 1 },
|
|
307
307
|
},
|
|
308
|
-
|
|
308
|
+
'merge queries with $or': {
|
|
309
309
|
target: { id: 1 },
|
|
310
310
|
source: { $or: [{ id: { $in: [1, 3] } }], id: 2 },
|
|
311
|
-
options: { defaultHandle:
|
|
311
|
+
options: { defaultHandle: 'combine', useLogicalConjunction: true },
|
|
312
312
|
expected: { id: 1, $or: [{ id: { $in: [1, 3] } }, { id: 2 }] },
|
|
313
313
|
},
|
|
314
|
-
|
|
314
|
+
'merge queries with existing $or': {
|
|
315
315
|
target: { id: 1 },
|
|
316
316
|
source: { $or: [{ id: { $in: [1, 3] } }], id: 2 },
|
|
317
|
-
options: { defaultHandle:
|
|
317
|
+
options: { defaultHandle: 'combine', useLogicalConjunction: true },
|
|
318
318
|
expected: { id: 1, $or: [{ id: { $in: [1, 3] } }, { id: 2 }] },
|
|
319
319
|
},
|
|
320
|
-
|
|
320
|
+
'merge queries with existing $and': {
|
|
321
321
|
target: { id: 1 },
|
|
322
322
|
source: { $and: [{ id: 2 }] },
|
|
323
|
-
options: { defaultHandle:
|
|
323
|
+
options: { defaultHandle: 'combine', useLogicalConjunction: true },
|
|
324
324
|
expected: { id: 1, $or: [{ $and: [{ id: 2 }] }] },
|
|
325
325
|
},
|
|
326
|
-
|
|
326
|
+
'merge queries with existing $or in target': {
|
|
327
327
|
target: { $or: [{ id: 1 }], id: 3 },
|
|
328
328
|
source: { id: 2 },
|
|
329
|
-
options: { defaultHandle:
|
|
329
|
+
options: { defaultHandle: 'combine', useLogicalConjunction: true },
|
|
330
330
|
expected: { $or: [{ id: 1 }, { id: 2 }], id: 3 },
|
|
331
331
|
},
|
|
332
|
-
}
|
|
332
|
+
}
|
|
333
333
|
for (const key in passingPairs) {
|
|
334
|
-
const { target, source, options, expected } = passingPairs[key]
|
|
334
|
+
const { target, source, options, expected } = (passingPairs as any)[key]
|
|
335
335
|
it(`'${key}'`, function () {
|
|
336
336
|
expect(
|
|
337
337
|
mergeQuery(target, source, Object.assign({ service }, options)),
|
|
338
|
-
).toEqual(expected)
|
|
339
|
-
})
|
|
338
|
+
).toEqual(expected)
|
|
339
|
+
})
|
|
340
340
|
}
|
|
341
|
-
})
|
|
342
|
-
})
|
|
341
|
+
})
|
|
342
|
+
})
|
|
343
343
|
|
|
344
|
-
describe(
|
|
345
|
-
const app = feathers()
|
|
344
|
+
describe('simple objects passing with handle intersect', function () {
|
|
345
|
+
const app = feathers()
|
|
346
346
|
app.use(
|
|
347
|
-
|
|
347
|
+
'/service',
|
|
348
348
|
new MemoryService({
|
|
349
349
|
paginate: { default: 10, max: 100 },
|
|
350
|
-
whitelist: [
|
|
350
|
+
whitelist: ['$and'],
|
|
351
351
|
filters: {
|
|
352
|
-
...filterArray(
|
|
352
|
+
...filterArray('$and'),
|
|
353
353
|
},
|
|
354
354
|
}),
|
|
355
|
-
)
|
|
356
|
-
const service = app.service(
|
|
355
|
+
)
|
|
356
|
+
const service = app.service('/service')
|
|
357
357
|
const passingPairs = {
|
|
358
|
-
|
|
358
|
+
'intersect number and overlapping $in': {
|
|
359
359
|
target: { id: 1 },
|
|
360
360
|
source: { id: { $in: [1, 3] } },
|
|
361
|
-
options: { defaultHandle:
|
|
361
|
+
options: { defaultHandle: 'intersect', useLogicalConjunction: false },
|
|
362
362
|
expected: { id: 1 },
|
|
363
363
|
},
|
|
364
|
-
|
|
364
|
+
'intersect $in and $in with overlapping': {
|
|
365
365
|
target: { id: { $in: [1, 2] } },
|
|
366
366
|
source: { id: { $in: [1, 3] } },
|
|
367
|
-
options: { defaultHandle:
|
|
367
|
+
options: { defaultHandle: 'intersect', useLogicalConjunction: false },
|
|
368
368
|
expected: { id: 1 },
|
|
369
369
|
},
|
|
370
|
-
|
|
370
|
+
'$limit for target stays the same': {
|
|
371
371
|
target: { $limit: 50, $skip: 10, $sort: { id: 1 } },
|
|
372
372
|
source: { id: 1 },
|
|
373
|
-
options: { defaultHandle:
|
|
373
|
+
options: { defaultHandle: 'intersect', useLogicalConjunction: false },
|
|
374
374
|
expected: { id: 1, $limit: 50, $skip: 10, $sort: { id: 1 } },
|
|
375
375
|
},
|
|
376
|
-
|
|
376
|
+
'$limit gets overridden': {
|
|
377
377
|
target: { $limit: 50, $skip: 10, $sort: { id: 1 } },
|
|
378
378
|
source: { $limit: 10, id: 1 },
|
|
379
|
-
options: { defaultHandle:
|
|
379
|
+
options: { defaultHandle: 'intersect', useLogicalConjunction: false },
|
|
380
380
|
expected: { id: 1, $limit: 10, $skip: 10, $sort: { id: 1 } },
|
|
381
381
|
},
|
|
382
|
-
|
|
382
|
+
'intersects two $or queries': {
|
|
383
383
|
target: { $or: [{ id: 1 }, { id: 2 }] },
|
|
384
384
|
source: { $or: [{ id: 3 }] },
|
|
385
|
-
options: { defaultHandle:
|
|
385
|
+
options: { defaultHandle: 'intersect', useLogicalConjunction: false },
|
|
386
386
|
expected: {
|
|
387
387
|
$and: [{ $or: [{ id: 1 }, { id: 2 }] }, { $or: [{ id: 3 }] }],
|
|
388
388
|
},
|
|
389
389
|
},
|
|
390
|
-
|
|
390
|
+
'intersects two $and queries': {
|
|
391
391
|
target: { $and: [{ id: 1 }, { id: 2 }] },
|
|
392
392
|
source: { $and: [{ id: 3 }] },
|
|
393
|
-
options: { defaultHandle:
|
|
393
|
+
options: { defaultHandle: 'intersect', useLogicalConjunction: false },
|
|
394
394
|
expected: { $and: [{ id: 1 }, { id: 2 }, { id: 3 }] },
|
|
395
395
|
},
|
|
396
|
-
|
|
396
|
+
'intersect $or and $and queries': {
|
|
397
397
|
target: { $or: [{ id: 1 }, { id: 2 }], $and: [{ id: 4 }] },
|
|
398
398
|
source: { $or: [{ id: 3 }], $and: [{ id: 5 }] },
|
|
399
|
-
options: { defaultHandle:
|
|
399
|
+
options: { defaultHandle: 'intersect', useLogicalConjunction: false },
|
|
400
400
|
expected: {
|
|
401
401
|
$and: [
|
|
402
402
|
{ id: 4 },
|
|
@@ -406,157 +406,157 @@ if (import.meta.vitest) {
|
|
|
406
406
|
],
|
|
407
407
|
},
|
|
408
408
|
},
|
|
409
|
-
|
|
409
|
+
'cleans up $and with empty entries': {
|
|
410
410
|
target: { $and: [{}, { id: 1 }, { id: 1 }, { id: 2 }] },
|
|
411
411
|
source: { $and: [{}, { id: 2 }] },
|
|
412
|
-
options: { defaultHandle:
|
|
412
|
+
options: { defaultHandle: 'intersect', useLogicalConjunction: false },
|
|
413
413
|
expected: { $and: [{ id: 1 }, { id: 2 }] },
|
|
414
414
|
},
|
|
415
|
-
|
|
415
|
+
'removes duplicate entries in $and': {
|
|
416
416
|
target: { $and: [{ id: 1 }, { id: 1 }, { id: 2 }] },
|
|
417
417
|
source: { $and: [{ id: 2 }] },
|
|
418
|
-
options: { defaultHandle:
|
|
418
|
+
options: { defaultHandle: 'intersect', useLogicalConjunction: false },
|
|
419
419
|
expected: { $and: [{ id: 1 }, { id: 2 }] },
|
|
420
420
|
},
|
|
421
|
-
|
|
421
|
+
'removes unnecessary $or in target with intersect': {
|
|
422
422
|
target: { $or: [{ id: 1 }, {}] },
|
|
423
|
-
source: { hi:
|
|
424
|
-
options: { defaultHandle:
|
|
425
|
-
expected: { hi:
|
|
423
|
+
source: { hi: 'test' },
|
|
424
|
+
options: { defaultHandle: 'intersect', useLogicalConjunction: false },
|
|
425
|
+
expected: { hi: 'test' },
|
|
426
426
|
},
|
|
427
|
-
|
|
428
|
-
target: { hi:
|
|
427
|
+
'removes unnecessary $or in source with intersect': {
|
|
428
|
+
target: { hi: 'test' },
|
|
429
429
|
source: { $or: [{ id: 1 }, {}] },
|
|
430
|
-
options: { defaultHandle:
|
|
431
|
-
expected: { hi:
|
|
430
|
+
options: { defaultHandle: 'intersect', useLogicalConjunction: false },
|
|
431
|
+
expected: { hi: 'test' },
|
|
432
432
|
},
|
|
433
|
-
|
|
434
|
-
target: { id: { $in: [1, 2, 3] }, status:
|
|
433
|
+
'$in and other props': {
|
|
434
|
+
target: { id: { $in: [1, 2, 3] }, status: 'complete' },
|
|
435
435
|
source: {
|
|
436
436
|
id: { $in: [2, 3, 4] },
|
|
437
|
-
status: { $in: [
|
|
437
|
+
status: { $in: ['complete', 'pending', 'draft'] },
|
|
438
438
|
},
|
|
439
|
-
options: { defaultHandle:
|
|
440
|
-
expected: { id: { $in: [2, 3] }, status:
|
|
439
|
+
options: { defaultHandle: 'intersect', useLogicalConjunction: false },
|
|
440
|
+
expected: { id: { $in: [2, 3] }, status: 'complete' },
|
|
441
441
|
},
|
|
442
|
-
}
|
|
442
|
+
}
|
|
443
443
|
for (const key in passingPairs) {
|
|
444
|
-
const { target, source, options, expected } = passingPairs[key]
|
|
444
|
+
const { target, source, options, expected } = (passingPairs as any)[key]
|
|
445
445
|
it(`'${key}'`, function () {
|
|
446
446
|
expect(
|
|
447
447
|
mergeQuery(target, source, Object.assign({ service }, options)),
|
|
448
|
-
).toEqual(expected)
|
|
449
|
-
})
|
|
448
|
+
).toEqual(expected)
|
|
449
|
+
})
|
|
450
450
|
}
|
|
451
451
|
|
|
452
|
-
describe(
|
|
453
|
-
const app = feathers()
|
|
452
|
+
describe('with useLogicalConjunction: true', function () {
|
|
453
|
+
const app = feathers()
|
|
454
454
|
app.use(
|
|
455
|
-
|
|
455
|
+
'/service',
|
|
456
456
|
new MemoryService({
|
|
457
457
|
paginate: { default: 10, max: 100 },
|
|
458
|
-
operators: [
|
|
458
|
+
operators: ['$and'],
|
|
459
459
|
filters: {
|
|
460
|
-
...filterArray(
|
|
460
|
+
...filterArray('$and'),
|
|
461
461
|
},
|
|
462
462
|
}),
|
|
463
|
-
)
|
|
464
|
-
const service = app.service(
|
|
463
|
+
)
|
|
464
|
+
const service = app.service('/service')
|
|
465
465
|
const passingPairs = {
|
|
466
|
-
|
|
466
|
+
'merge empty source': {
|
|
467
467
|
target: { id: 1 },
|
|
468
468
|
source: {},
|
|
469
|
-
options: { defaultHandle:
|
|
469
|
+
options: { defaultHandle: 'intersect', useLogicalConjunction: true },
|
|
470
470
|
expected: { id: 1 },
|
|
471
471
|
},
|
|
472
|
-
|
|
472
|
+
'merge empty target': {
|
|
473
473
|
target: {},
|
|
474
474
|
source: { id: 1 },
|
|
475
|
-
options: { defaultHandle:
|
|
475
|
+
options: { defaultHandle: 'intersect', useLogicalConjunction: true },
|
|
476
476
|
expected: { id: 1 },
|
|
477
477
|
},
|
|
478
|
-
|
|
478
|
+
'merge queries with $and': {
|
|
479
479
|
target: { id: 1 },
|
|
480
480
|
source: { id: { $in: [1, 3] } },
|
|
481
|
-
options: { defaultHandle:
|
|
481
|
+
options: { defaultHandle: 'intersect', useLogicalConjunction: true },
|
|
482
482
|
expected: { id: 1, $and: [{ id: { $in: [1, 3] } }] },
|
|
483
483
|
},
|
|
484
|
-
|
|
484
|
+
'merge queries with existing $and': {
|
|
485
485
|
target: { id: 1 },
|
|
486
486
|
source: { $and: [{ id: { $in: [1, 3] } }], id: 2 },
|
|
487
|
-
options: { defaultHandle:
|
|
487
|
+
options: { defaultHandle: 'intersect', useLogicalConjunction: true },
|
|
488
488
|
expected: { id: 1, $and: [{ id: { $in: [1, 3] } }, { id: 2 }] },
|
|
489
489
|
},
|
|
490
|
-
|
|
490
|
+
'merge queries with existing $or': {
|
|
491
491
|
target: { id: 1 },
|
|
492
492
|
source: { $or: [{ id: 2 }] },
|
|
493
|
-
options: { defaultHandle:
|
|
493
|
+
options: { defaultHandle: 'intersect', useLogicalConjunction: true },
|
|
494
494
|
expected: { id: 1, $and: [{ $or: [{ id: 2 }] }] },
|
|
495
495
|
},
|
|
496
|
-
|
|
496
|
+
'merge queries with existing $and in target': {
|
|
497
497
|
target: { $and: [{ id: 1 }], id: 3 },
|
|
498
498
|
source: { id: 2 },
|
|
499
|
-
options: { defaultHandle:
|
|
499
|
+
options: { defaultHandle: 'intersect', useLogicalConjunction: true },
|
|
500
500
|
expected: { $and: [{ id: 1 }, { id: 2 }], id: 3 },
|
|
501
501
|
},
|
|
502
|
-
|
|
502
|
+
'merge queries without $and': {
|
|
503
503
|
target: { id: 1 },
|
|
504
504
|
source: { userId: 2 },
|
|
505
|
-
options: { defaultHandle:
|
|
505
|
+
options: { defaultHandle: 'intersect', useLogicalConjunction: true },
|
|
506
506
|
expected: { id: 1, userId: 2 },
|
|
507
507
|
},
|
|
508
|
-
|
|
508
|
+
'skip merge if source is in target': {
|
|
509
509
|
target: { id: 1, userId: 2 },
|
|
510
510
|
source: { userId: 2 },
|
|
511
|
-
options: { defaultHandle:
|
|
511
|
+
options: { defaultHandle: 'intersect', useLogicalConjunction: true },
|
|
512
512
|
expected: { id: 1, userId: 2 },
|
|
513
513
|
},
|
|
514
|
-
|
|
514
|
+
'skip merge if target is in source': {
|
|
515
515
|
target: { userId: 2 },
|
|
516
516
|
source: { id: 1, userId: 2 },
|
|
517
|
-
options: { defaultHandle:
|
|
517
|
+
options: { defaultHandle: 'intersect', useLogicalConjunction: true },
|
|
518
518
|
expected: { id: 1, userId: 2 },
|
|
519
519
|
},
|
|
520
|
-
}
|
|
520
|
+
}
|
|
521
521
|
for (const key in passingPairs) {
|
|
522
|
-
const { target, source, options, expected } = passingPairs[key]
|
|
522
|
+
const { target, source, options, expected } = (passingPairs as any)[key]
|
|
523
523
|
it(`'${key}'`, function () {
|
|
524
524
|
expect(
|
|
525
525
|
mergeQuery(target, source, Object.assign({ service }, options)),
|
|
526
|
-
).toEqual(expected)
|
|
527
|
-
})
|
|
526
|
+
).toEqual(expected)
|
|
527
|
+
})
|
|
528
528
|
}
|
|
529
|
-
})
|
|
530
|
-
})
|
|
529
|
+
})
|
|
530
|
+
})
|
|
531
531
|
|
|
532
|
-
describe(
|
|
532
|
+
describe('simple objects failing', function () {
|
|
533
533
|
const failingPairs = {
|
|
534
|
-
|
|
534
|
+
'intersect two numbers': {
|
|
535
535
|
target: { id: 1 },
|
|
536
536
|
source: { id: 2 },
|
|
537
|
-
options: { defaultHandle:
|
|
537
|
+
options: { defaultHandle: 'intersect', useLogicalConjunction: false },
|
|
538
538
|
},
|
|
539
|
-
|
|
539
|
+
'intersect booleans': {
|
|
540
540
|
target: { test: true },
|
|
541
541
|
source: { test: false },
|
|
542
|
-
options: { defaultHandle:
|
|
542
|
+
options: { defaultHandle: 'intersect', useLogicalConjunction: false },
|
|
543
543
|
},
|
|
544
|
-
|
|
544
|
+
'intersect number and $in': {
|
|
545
545
|
target: { id: 1 },
|
|
546
546
|
source: { id: { $in: [2, 3] } },
|
|
547
|
-
options: { defaultHandle:
|
|
547
|
+
options: { defaultHandle: 'intersect', useLogicalConjunction: false },
|
|
548
548
|
},
|
|
549
|
-
|
|
549
|
+
'intersect two $in': {
|
|
550
550
|
target: { id: { $in: [1, 3] } },
|
|
551
551
|
source: { id: { $in: [2, 4] } },
|
|
552
|
-
options: { defaultHandle:
|
|
552
|
+
options: { defaultHandle: 'intersect', useLogicalConjunction: false },
|
|
553
553
|
},
|
|
554
|
-
}
|
|
554
|
+
}
|
|
555
555
|
for (const key in failingPairs) {
|
|
556
|
-
const { target, source, options } = failingPairs[key]
|
|
556
|
+
const { target, source, options } = (failingPairs as any)[key]
|
|
557
557
|
it(`'${key}'`, function () {
|
|
558
|
-
expect(() => mergeQuery(target, source, options)).toThrow(Forbidden)
|
|
559
|
-
})
|
|
558
|
+
expect(() => mergeQuery(target, source, options)).toThrow(Forbidden)
|
|
559
|
+
})
|
|
560
560
|
}
|
|
561
|
-
})
|
|
561
|
+
})
|
|
562
562
|
}
|