@flowerforce/flowerbase 1.7.5 → 1.7.6-beta.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.
Files changed (62) hide show
  1. package/dist/auth/controller.d.ts.map +1 -1
  2. package/dist/auth/controller.js +11 -10
  3. package/dist/auth/plugins/jwt.js +1 -1
  4. package/dist/auth/providers/anon-user/controller.js +1 -1
  5. package/dist/auth/providers/custom-function/controller.d.ts.map +1 -1
  6. package/dist/auth/providers/custom-function/controller.js +36 -10
  7. package/dist/auth/providers/local-userpass/controller.d.ts.map +1 -1
  8. package/dist/auth/providers/local-userpass/controller.js +15 -14
  9. package/dist/auth/utils.d.ts +1 -0
  10. package/dist/auth/utils.d.ts.map +1 -1
  11. package/dist/constants.d.ts +1 -0
  12. package/dist/constants.d.ts.map +1 -1
  13. package/dist/constants.js +4 -3
  14. package/dist/features/triggers/index.js +1 -1
  15. package/dist/features/triggers/utils.d.ts.map +1 -1
  16. package/dist/features/triggers/utils.js +38 -30
  17. package/dist/monitoring/routes/users.d.ts.map +1 -1
  18. package/dist/monitoring/routes/users.js +7 -6
  19. package/dist/monitoring/utils.d.ts.map +1 -1
  20. package/dist/monitoring/utils.js +5 -4
  21. package/dist/services/api/index.d.ts +4 -0
  22. package/dist/services/api/index.d.ts.map +1 -1
  23. package/dist/services/api/utils.d.ts +1 -0
  24. package/dist/services/api/utils.d.ts.map +1 -1
  25. package/dist/services/index.d.ts +4 -0
  26. package/dist/services/index.d.ts.map +1 -1
  27. package/dist/shared/handleUserDeletion.js +1 -1
  28. package/dist/shared/handleUserRegistration.js +2 -2
  29. package/dist/utils/context/helpers.d.ts +12 -0
  30. package/dist/utils/context/helpers.d.ts.map +1 -1
  31. package/dist/utils/initializer/exposeRoutes.js +1 -1
  32. package/dist/utils/rules-matcher/interface.d.ts +5 -1
  33. package/dist/utils/rules-matcher/interface.d.ts.map +1 -1
  34. package/dist/utils/rules-matcher/interface.js +2 -0
  35. package/dist/utils/rules-matcher/utils.d.ts.map +1 -1
  36. package/dist/utils/rules-matcher/utils.js +51 -16
  37. package/package.json +1 -1
  38. package/src/auth/__tests__/controller.test.ts +1 -0
  39. package/src/auth/controller.ts +12 -11
  40. package/src/auth/plugins/jwt.ts +2 -2
  41. package/src/auth/providers/anon-user/__tests__/controller.test.ts +1 -0
  42. package/src/auth/providers/anon-user/controller.ts +2 -2
  43. package/src/auth/providers/custom-function/controller.ts +39 -12
  44. package/src/auth/providers/local-userpass/controller.ts +16 -15
  45. package/src/auth/utils.ts +1 -0
  46. package/src/constants.ts +3 -2
  47. package/src/features/triggers/__tests__/index.test.ts +1 -0
  48. package/src/features/triggers/index.ts +2 -2
  49. package/src/features/triggers/utils.ts +42 -31
  50. package/src/monitoring/routes/users.ts +8 -7
  51. package/src/monitoring/ui.css +5 -1
  52. package/src/monitoring/ui.events.js +2 -2
  53. package/src/monitoring/ui.shared.js +2 -1
  54. package/src/monitoring/utils.ts +6 -5
  55. package/src/shared/handleUserDeletion.ts +2 -2
  56. package/src/shared/handleUserRegistration.ts +3 -3
  57. package/src/utils/__tests__/operators.test.ts +24 -0
  58. package/src/utils/__tests__/rule.test.ts +39 -0
  59. package/src/utils/__tests__/rulesMatcherInterfaces.test.ts +2 -0
  60. package/src/utils/initializer/exposeRoutes.ts +2 -2
  61. package/src/utils/rules-matcher/interface.ts +5 -1
  62. package/src/utils/rules-matcher/utils.ts +78 -32
@@ -1,3 +1,4 @@
1
+ import { ObjectId } from 'mongodb'
1
2
  import { operators } from '../rules-matcher/utils'
2
3
 
3
4
  describe('operators', () => {
@@ -8,10 +9,15 @@ describe('operators', () => {
8
9
  it('should check equals values', () => {
9
10
  expect(operators.$eq('a', 'a')).toBe(true)
10
11
  expect(!operators.$eq('a', 'b')).toBe(true)
12
+ const id = new ObjectId()
13
+ expect(operators.$eq(id, id.toHexString())).toBe(true)
14
+ expect(operators.$eq(id, new ObjectId(id.toHexString()))).toBe(true)
11
15
  })
12
16
  it('should check different values', () => {
13
17
  expect(operators.$ne('a', 'a')).toBe(false)
14
18
  expect(operators.$ne('a', 'b')).toBe(true)
19
+ const id = new ObjectId()
20
+ expect(operators.$ne(id, id.toHexString())).toBe(false)
15
21
  })
16
22
  it('should check string length values', () => {
17
23
  // test $strGt
@@ -69,6 +75,8 @@ describe('operators', () => {
69
75
  expect(operators.$in([3, 4], [3, 4, 5])).toBe(true)
70
76
  expect(operators.$in([3, 6], [3, 4, 5])).toBe(true)
71
77
  expect(operators.$in({ name: 'ciao' }, [{ name: 'ciao' }, 4, 5])).toBe(false)
78
+ const id = new ObjectId()
79
+ expect(operators.$in(id, [id.toHexString()])).toBe(true)
72
80
  })
73
81
  it("should check if a value isn't in an array", () => {
74
82
  expect(operators.$nin(2, [3])).toBe(true)
@@ -76,6 +84,8 @@ describe('operators', () => {
76
84
  expect(operators.$nin([3, 4], [3, 4, 5])).toBe(false)
77
85
  expect(operators.$nin([3, 6], [3, 4, 5])).toBe(false)
78
86
  expect(operators.$nin({ name: 'ciao' }, [{ name: 'ciao' }, 4, 5])).toBe(true)
87
+ const id = new ObjectId()
88
+ expect(operators.$nin(id, [id.toHexString()])).toBe(false)
79
89
  })
80
90
  it('should find all values in an array', () => {
81
91
  expect(operators.$all(2, [3])).toBe(false)
@@ -85,6 +95,8 @@ describe('operators', () => {
85
95
  expect(operators.$all([3, 6], [3, 4, 5])).toBe(false)
86
96
  expect(operators.$all({ name: 'ciao' }, [{ name: 'ciao' }, 4, 5])).toBe(false)
87
97
  expect(operators.$all([{ name: 'ciao' }, 4, 5], [{ name: 'ciao' }, 4, 5])).toBe(false)
98
+ const id = new ObjectId()
99
+ expect(operators.$all([id], [id.toHexString()])).toBe(true)
88
100
  })
89
101
  it('should check array size', () => {
90
102
  expect(operators.$size([1, 2, 3], 3)).toBe(true)
@@ -100,4 +112,16 @@ describe('operators', () => {
100
112
  expect(operators.$regex('1234567890', numberRegex)).toBe(true)
101
113
  expect(operators.$regex('12345r', numberRegex)).toBe(false)
102
114
  })
115
+
116
+ it('should support %stringToOid conversion operator', () => {
117
+ const id = new ObjectId()
118
+ expect(operators['%stringToOid'](id, id.toHexString())).toBe(true)
119
+ expect(operators['%stringToOid'](id, 'not-an-object-id')).toBe(false)
120
+ })
121
+
122
+ it('should support %oidToString conversion operator', () => {
123
+ const id = new ObjectId()
124
+ expect(operators['%oidToString'](id.toHexString(), id)).toBe(true)
125
+ expect(operators['%oidToString']('not-matching', id)).toBe(false)
126
+ })
103
127
  })
@@ -1,3 +1,4 @@
1
+ import { ObjectId } from 'mongodb'
1
2
  import rulesMatcherUtils from '../rules-matcher/utils'
2
3
 
3
4
  describe('rule function', () => {
@@ -46,4 +47,42 @@ describe('rule function', () => {
46
47
  rulesMatcherUtils.rule(missingOperatorBlock, mockData, mockOptions)
47
48
  }).toThrow('Error missing operator:$notFoundOperator')
48
49
  })
50
+
51
+ it('should support %stringToOid with $ref values', () => {
52
+ const companyId = new ObjectId()
53
+ const data = {
54
+ user: {
55
+ _id: companyId
56
+ },
57
+ auth: {
58
+ company: companyId.toHexString()
59
+ }
60
+ }
61
+
62
+ const result = rulesMatcherUtils.rule({ _id: { '%stringToOid': '$ref:auth.company' } }, data, {
63
+ prefix: 'user'
64
+ })
65
+
66
+ expect(result.valid).toBe(true)
67
+ expect(result.name).toBe('user._id___%stringToOid')
68
+ })
69
+
70
+ it('should support %oidToString with $ref values', () => {
71
+ const authId = new ObjectId()
72
+ const data = {
73
+ user: {
74
+ authId: authId.toHexString()
75
+ },
76
+ auth: {
77
+ id: authId
78
+ }
79
+ }
80
+
81
+ const result = rulesMatcherUtils.rule({ authId: { '%oidToString': '$ref:auth.id' } }, data, {
82
+ prefix: 'user'
83
+ })
84
+
85
+ expect(result.valid).toBe(true)
86
+ expect(result.name).toBe('user.authId___%oidToString')
87
+ })
49
88
  })
@@ -22,6 +22,8 @@ describe('Enums and Types', () => {
22
22
  expect(RulesOperators.$nin).toBe('$nin')
23
23
  expect(RulesOperators.$all).toBe('$all')
24
24
  expect(RulesOperators.$regex).toBe('$regex')
25
+ expect(RulesOperators['%stringToOid']).toBe('%stringToOid')
26
+ expect(RulesOperators['%oidToString']).toBe('%oidToString')
25
27
  })
26
28
 
27
29
  it('should validate RulesOperatorsInArray type', () => {
@@ -2,7 +2,7 @@ import { uptime } from 'node:process'
2
2
  import { FastifyInstance } from 'fastify'
3
3
  import { RegistrationDto } from '../../auth/providers/local-userpass/dtos'
4
4
  import { AUTH_ENDPOINTS, REGISTRATION_SCHEMA } from '../../auth/utils'
5
- import { API_VERSION, AUTH_CONFIG, DB_NAME, DEFAULT_CONFIG } from '../../constants'
5
+ import { API_VERSION, AUTH_CONFIG, AUTH_DB_NAME, DEFAULT_CONFIG } from '../../constants'
6
6
  import { PROVIDER } from '../../shared/models/handleUserRegistration.model'
7
7
  import { hashPassword } from '../crypto'
8
8
 
@@ -46,7 +46,7 @@ export const exposeRoutes = async (fastify: FastifyInstance) => {
46
46
  schema: REGISTRATION_SCHEMA
47
47
  }, async function (req, res) {
48
48
  const { authCollection } = AUTH_CONFIG
49
- const db = fastify.mongo.client.db(DB_NAME)
49
+ const db = fastify.mongo.client.db(AUTH_DB_NAME)
50
50
  const { email, password } = req.body
51
51
  const hashedPassword = await hashPassword(password)
52
52
  const now = new Date()
@@ -331,6 +331,8 @@ export type Operators = {
331
331
  * @returns
332
332
  */
333
333
  $regex: OperatorsFunction
334
+ '%stringToOid': OperatorsFunction
335
+ '%oidToString': OperatorsFunction
334
336
  }
335
337
 
336
338
  export enum RulesOperators {
@@ -349,7 +351,9 @@ export enum RulesOperators {
349
351
  $nin = '$nin',
350
352
  $all = '$all',
351
353
  $size = '$size',
352
- $regex = '$regex'
354
+ $regex = '$regex',
355
+ '%stringToOid' = '%stringToOid',
356
+ '%oidToString' = '%oidToString'
353
357
  }
354
358
 
355
359
  export type RulesOperatorsInArray<T> = Partial<{
@@ -1,9 +1,59 @@
1
+ import { ObjectId } from 'bson'
1
2
  import _get from 'lodash/get'
2
- import _intersection from 'lodash/intersection'
3
3
  import _trimStart from 'lodash/trimStart'
4
4
  import { Operators, RulesMatcherUtils, RulesObject } from './interface'
5
5
 
6
6
  const EMPTY_STRING_REGEXP = /^\s*$/
7
+ const HEX_24_REGEXP = /^[a-fA-F0-9]{24}$/
8
+
9
+ const toObjectIdHex = (value: unknown): string | null => {
10
+ if (value instanceof ObjectId) {
11
+ return value.toHexString()
12
+ }
13
+
14
+ if (typeof value === 'string') {
15
+ if (!HEX_24_REGEXP.test(value) || !ObjectId.isValid(value)) {
16
+ return null
17
+ }
18
+ return new ObjectId(value).toHexString()
19
+ }
20
+
21
+ if (!value || typeof value !== 'object') {
22
+ return null
23
+ }
24
+
25
+ const maybeObjectId = value as { _bsontype?: string; toHexString?: () => string }
26
+ if (maybeObjectId._bsontype === 'ObjectId' && typeof maybeObjectId.toHexString === 'function') {
27
+ const hex = maybeObjectId.toHexString()
28
+ return HEX_24_REGEXP.test(hex) ? hex.toLowerCase() : null
29
+ }
30
+
31
+ return null
32
+ }
33
+
34
+ const areSemanticallyEqual = (left: unknown, right: unknown): boolean => {
35
+ const leftOid = toObjectIdHex(left)
36
+ const rightOid = toObjectIdHex(right)
37
+
38
+ if (leftOid || rightOid) {
39
+ return leftOid !== null && rightOid !== null && leftOid === rightOid
40
+ }
41
+
42
+ return left === right
43
+ }
44
+
45
+ const includesWithSemanticEquality = (value: unknown, candidate: unknown): boolean =>
46
+ rulesMatcherUtils
47
+ .forceArray(candidate)
48
+ .some((item) =>
49
+ rulesMatcherUtils
50
+ .forceArray(value)
51
+ .some((sourceItem) =>
52
+ rulesMatcherUtils.forceArray(item).some((candidateItem) =>
53
+ areSemanticallyEqual(sourceItem, candidateItem)
54
+ )
55
+ )
56
+ )
7
57
 
8
58
  /**
9
59
  * Defines a utility object named rulesMatcherUtils, which contains various helper functions used for processing rules and data in a rule-matching context.
@@ -24,10 +74,10 @@ const rulesMatcherUtils: RulesMatcherUtils = {
24
74
  const valueRef =
25
75
  value && String(value).indexOf('$ref:') === 0
26
76
  ? _get(
27
- data,
28
- rulesMatcherUtils.getPath(value.replace('$ref:', ''), prefix),
29
- undefined
30
- )
77
+ data,
78
+ rulesMatcherUtils.getPath(value.replace('$ref:', ''), prefix),
79
+ undefined
80
+ )
31
81
  : value
32
82
 
33
83
  if (!operators[op]) {
@@ -230,9 +280,9 @@ const rulesMatcherUtils: RulesMatcherUtils = {
230
280
  export const operators: Operators = {
231
281
  $exists: (a, b) => !rulesMatcherUtils.isEmpty(a) === b,
232
282
 
233
- $eq: (a, b) => a === b,
283
+ $eq: (a, b) => areSemanticallyEqual(a, b),
234
284
 
235
- $ne: (a, b) => a !== b,
285
+ $ne: (a, b) => !areSemanticallyEqual(a, b),
236
286
 
237
287
  $gt: (a, b) => rulesMatcherUtils.forceNumber(a) > parseFloat(b),
238
288
 
@@ -250,39 +300,35 @@ export const operators: Operators = {
250
300
 
251
301
  $strLte: (a, b) => String(a || '').length <= parseFloat(b),
252
302
 
253
- $in: (a, b) =>
254
- rulesMatcherUtils
255
- .forceArray(b)
256
- .some(
257
- (c) =>
258
- _intersection(rulesMatcherUtils.forceArray(a), rulesMatcherUtils.forceArray(c))
259
- .length
260
- ),
261
-
262
- $nin: (a, b) =>
263
- !rulesMatcherUtils
264
- .forceArray(b)
265
- .some(
266
- (c) =>
267
- _intersection(rulesMatcherUtils.forceArray(a), rulesMatcherUtils.forceArray(c))
268
- .length
269
- ),
303
+ $in: (a, b) => includesWithSemanticEquality(a, b),
304
+
305
+ $nin: (a, b) => !includesWithSemanticEquality(a, b),
270
306
 
271
307
  $all: (a, b) =>
272
- rulesMatcherUtils
273
- .forceArray(b)
274
- .every(
275
- (c) =>
276
- _intersection(rulesMatcherUtils.forceArray(a), rulesMatcherUtils.forceArray(c))
277
- .length
278
- ),
308
+ rulesMatcherUtils.forceArray(b).every((candidate) =>
309
+ rulesMatcherUtils
310
+ .forceArray(a)
311
+ .some((value) =>
312
+ rulesMatcherUtils.forceArray(candidate).some((item) => areSemanticallyEqual(value, item))
313
+ )
314
+ ),
279
315
 
280
316
  $size: (a, b) => Array.isArray(a) && a.length === parseFloat(b),
281
317
 
282
318
  $regex: (a, b, opt) =>
283
319
  rulesMatcherUtils
284
320
  .forceArray(b)
285
- .some((c) => (c instanceof RegExp ? c.test(a) : new RegExp(c, opt).test(a)))
321
+ .some((c) => (c instanceof RegExp ? c.test(a) : new RegExp(c, opt).test(a))),
322
+
323
+ '%stringToOid': (a, b) => {
324
+ const converted = toObjectIdHex(b)
325
+ return converted !== null && areSemanticallyEqual(a, converted)
326
+ },
327
+
328
+ '%oidToString': (a, b) => {
329
+ const converted = toObjectIdHex(b)
330
+ return converted !== null && areSemanticallyEqual(a, converted)
331
+ }
286
332
  }
287
333
 
288
334
  // export default operators