@flowerforce/flowerbase 1.9.0 → 1.10.0
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/CHANGELOG.md +7 -0
- package/dist/utils/rules-matcher/utils.d.ts.map +1 -1
- package/dist/utils/rules-matcher/utils.js +7 -1
- package/package.json +1 -1
- package/src/services/mongodb-atlas/__tests__/utils.test.ts +52 -1
- package/src/utils/__tests__/evaluateExpression.test.ts +23 -0
- package/src/utils/__tests__/rule.test.ts +2 -2
- package/src/utils/__tests__/rulesMatcherUtils.test.ts +11 -2
- package/src/utils/rules-matcher/utils.ts +9 -1
package/CHANGELOG.md
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"utils.d.ts","sourceRoot":"","sources":["../../../src/utils/rules-matcher/utils.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,SAAS,EAAE,iBAAiB,EAAe,MAAM,aAAa,CAAA;
|
|
1
|
+
{"version":3,"file":"utils.d.ts","sourceRoot":"","sources":["../../../src/utils/rules-matcher/utils.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,SAAS,EAAE,iBAAiB,EAAe,MAAM,aAAa,CAAA;AA2EvE;;GAEG;AACH,QAAA,MAAM,iBAAiB,EAAE,iBAkOxB,CAAA;AAED;;GAEG;AACH,eAAO,MAAM,SAAS,EAAE,SAyDvB,CAAA;AAID,eAAe,iBAAiB,CAAA"}
|
|
@@ -45,6 +45,12 @@ const includesWithSemanticEquality = (value, candidate) => rulesMatcherUtils
|
|
|
45
45
|
.some((sourceItem) => rulesMatcherUtils
|
|
46
46
|
.forceArray(item)
|
|
47
47
|
.some((candidateItem) => areSemanticallyEqual(sourceItem, candidateItem))));
|
|
48
|
+
const hasScalarArrayMembershipMatch = (left, right) => {
|
|
49
|
+
if (Array.isArray(left) === Array.isArray(right)) {
|
|
50
|
+
return false;
|
|
51
|
+
}
|
|
52
|
+
return includesWithSemanticEquality(left, right);
|
|
53
|
+
};
|
|
48
54
|
const resolveRefPath = (data, refPath, prefix) => {
|
|
49
55
|
const exactMatch = (0, get_1.default)(data, refPath, undefined);
|
|
50
56
|
if (exactMatch !== undefined) {
|
|
@@ -241,7 +247,7 @@ const rulesMatcherUtils = {
|
|
|
241
247
|
exports.operators = {
|
|
242
248
|
$exists: (a, b) => !rulesMatcherUtils.isEmpty(a) === b,
|
|
243
249
|
'%exists': (a, b) => !rulesMatcherUtils.isEmpty(a) === b,
|
|
244
|
-
$eq: (a, b) => areSemanticallyEqual(a, b),
|
|
250
|
+
$eq: (a, b) => areSemanticallyEqual(a, b) || hasScalarArrayMembershipMatch(a, b),
|
|
245
251
|
$ne: (a, b) => !areSemanticallyEqual(a, b),
|
|
246
252
|
$gt: (a, b) => rulesMatcherUtils.forceNumber(a) > parseFloat(b),
|
|
247
253
|
$gte: (a, b) => rulesMatcherUtils.forceNumber(a) >= parseFloat(b),
|
package/package.json
CHANGED
|
@@ -1,7 +1,58 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import {
|
|
2
|
+
ensureClientPipelineStages,
|
|
3
|
+
getHiddenFieldsFromRulesConfig,
|
|
4
|
+
prependUnsetStage,
|
|
5
|
+
applyAccessControlToPipeline,
|
|
6
|
+
mergeProjections,
|
|
7
|
+
getValidRule
|
|
8
|
+
} from '../utils'
|
|
2
9
|
import { Role } from '../../../utils/roles/interface'
|
|
3
10
|
|
|
4
11
|
describe('MongoDB Atlas aggregate helpers', () => {
|
|
12
|
+
describe('getValidRule', () => {
|
|
13
|
+
it('matches legacy scalar apply_when syntax against array-valued custom data', () => {
|
|
14
|
+
const validRules = getValidRule({
|
|
15
|
+
filters: [
|
|
16
|
+
{
|
|
17
|
+
apply_when: {
|
|
18
|
+
'%%user.custom_data.roles': 'Admin'
|
|
19
|
+
},
|
|
20
|
+
query: {}
|
|
21
|
+
}
|
|
22
|
+
] as any,
|
|
23
|
+
user: {
|
|
24
|
+
custom_data: {
|
|
25
|
+
roles: ['Admin', 'SCM']
|
|
26
|
+
}
|
|
27
|
+
} as any,
|
|
28
|
+
record: null
|
|
29
|
+
})
|
|
30
|
+
|
|
31
|
+
expect(validRules).toHaveLength(1)
|
|
32
|
+
})
|
|
33
|
+
|
|
34
|
+
it('does not match legacy scalar apply_when syntax when the array does not contain the value', () => {
|
|
35
|
+
const validRules = getValidRule({
|
|
36
|
+
filters: [
|
|
37
|
+
{
|
|
38
|
+
apply_when: {
|
|
39
|
+
'%%user.custom_data.roles': 'Admin'
|
|
40
|
+
},
|
|
41
|
+
query: {}
|
|
42
|
+
}
|
|
43
|
+
] as any,
|
|
44
|
+
user: {
|
|
45
|
+
custom_data: {
|
|
46
|
+
roles: ['Editor']
|
|
47
|
+
}
|
|
48
|
+
} as any,
|
|
49
|
+
record: null
|
|
50
|
+
})
|
|
51
|
+
|
|
52
|
+
expect(validRules).toHaveLength(0)
|
|
53
|
+
})
|
|
54
|
+
})
|
|
55
|
+
|
|
5
56
|
describe('ensureClientPipelineStages', () => {
|
|
6
57
|
it('allows safe stages', () => {
|
|
7
58
|
expect(() =>
|
|
@@ -119,4 +119,27 @@ describe('evaluateExpression', () => {
|
|
|
119
119
|
})
|
|
120
120
|
)
|
|
121
121
|
})
|
|
122
|
+
|
|
123
|
+
it('supports scalar equality against array-valued custom data', async () => {
|
|
124
|
+
const expression = {
|
|
125
|
+
'%%user.custom_data.roles': 'Admin'
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
const params = {
|
|
129
|
+
type: 'read',
|
|
130
|
+
cursor: { _id: 'doc-1' },
|
|
131
|
+
expansions: {
|
|
132
|
+
'%%prevRoot': undefined
|
|
133
|
+
},
|
|
134
|
+
roles: []
|
|
135
|
+
} as Params
|
|
136
|
+
|
|
137
|
+
const user = {
|
|
138
|
+
custom_data: {
|
|
139
|
+
roles: ['Admin', 'SCM']
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
await expect(evaluateExpression(params, expression, user as never)).resolves.toBe(true)
|
|
144
|
+
})
|
|
122
145
|
})
|
|
@@ -86,7 +86,7 @@ describe('rule function', () => {
|
|
|
86
86
|
expect(result.name).toBe('user.authId___%oidToString')
|
|
87
87
|
})
|
|
88
88
|
|
|
89
|
-
it('
|
|
89
|
+
it('treats scalar equality as array membership in compact rules', () => {
|
|
90
90
|
const data = {
|
|
91
91
|
doc: {
|
|
92
92
|
owners: ['user-1', 'user-2']
|
|
@@ -97,7 +97,7 @@ describe('rule function', () => {
|
|
|
97
97
|
prefix: 'doc'
|
|
98
98
|
})
|
|
99
99
|
|
|
100
|
-
expect(result.valid).toBe(
|
|
100
|
+
expect(result.valid).toBe(true)
|
|
101
101
|
expect(result.name).toBe('doc.owners___$eq')
|
|
102
102
|
})
|
|
103
103
|
|
|
@@ -27,8 +27,8 @@ describe('rulesMatcherUtils', () => {
|
|
|
27
27
|
// isFunction
|
|
28
28
|
expect(isFunction(2)).toBe(false)
|
|
29
29
|
expect(isFunction('ciao')).toBe(false)
|
|
30
|
-
expect(isFunction(() => {})).toBe(true)
|
|
31
|
-
expect(isFunction(function test() {})).toBe(true)
|
|
30
|
+
expect(isFunction(() => { })).toBe(true)
|
|
31
|
+
expect(isFunction(function test() { })).toBe(true)
|
|
32
32
|
// isString
|
|
33
33
|
expect(isString(2)).toBe(false)
|
|
34
34
|
expect(isString('2')).toBe(true)
|
|
@@ -94,4 +94,13 @@ describe('rulesMatcherUtils', () => {
|
|
|
94
94
|
)
|
|
95
95
|
).toBe(false)
|
|
96
96
|
})
|
|
97
|
+
|
|
98
|
+
it('matches scalar equality against array-valued fields', () => {
|
|
99
|
+
const data = {
|
|
100
|
+
'%%user': { custom_data: { roles: ['Admin', 'SCM'] } }
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
expect(checkRule({ '%%user.custom_data.roles': 'Admin' } as any, data, {})).toBe(true)
|
|
104
|
+
expect(checkRule({ '%%user.custom_data.roles': 'Manager' } as any, data, {})).toBe(false)
|
|
105
|
+
})
|
|
97
106
|
})
|
|
@@ -58,6 +58,14 @@ const includesWithSemanticEquality = (value: unknown, candidate: unknown): boole
|
|
|
58
58
|
)
|
|
59
59
|
)
|
|
60
60
|
|
|
61
|
+
const hasScalarArrayMembershipMatch = (left: unknown, right: unknown): boolean => {
|
|
62
|
+
if (Array.isArray(left) === Array.isArray(right)) {
|
|
63
|
+
return false
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
return includesWithSemanticEquality(left, right)
|
|
67
|
+
}
|
|
68
|
+
|
|
61
69
|
const resolveRefPath = (data: unknown, refPath: string, prefix?: string): unknown => {
|
|
62
70
|
const exactMatch = _get(data, refPath, undefined)
|
|
63
71
|
|
|
@@ -306,7 +314,7 @@ export const operators: Operators = {
|
|
|
306
314
|
$exists: (a, b) => !rulesMatcherUtils.isEmpty(a) === b,
|
|
307
315
|
'%exists': (a, b) => !rulesMatcherUtils.isEmpty(a) === b,
|
|
308
316
|
|
|
309
|
-
$eq: (a, b) => areSemanticallyEqual(a, b),
|
|
317
|
+
$eq: (a, b) => areSemanticallyEqual(a, b) || hasScalarArrayMembershipMatch(a, b),
|
|
310
318
|
|
|
311
319
|
$ne: (a, b) => !areSemanticallyEqual(a, b),
|
|
312
320
|
|