@questwork/q-utilities 0.1.15 → 0.1.16

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.
@@ -84,20 +84,21 @@ __webpack_require__.d(__webpack_exports__, {
84
84
  sanitizeText: () => (/* reexport */ sanitizeText),
85
85
  stringFormatter: () => (/* reexport */ stringFormatter),
86
86
  stringHelper: () => (/* reexport */ stringHelper),
87
+ tenantPlugin: () => (/* reexport */ tenantPlugin),
87
88
  trackingPlugin: () => (/* reexport */ trackingPlugin)
88
89
  });
89
90
 
90
91
  ;// ./lib/helpers/authorize/authorize.js
91
- function authorize({ allowOwner, query = {}, required, user }) {
92
+ function authorize({ allowCoordinator, allowOwner, query = {}, required, user }) {
92
93
  if (!user) {
93
94
  throw new Error('Require login.')
94
95
  }
95
96
  if (!user.permission) {
96
- throw new Error('You do not have any permission.')
97
+ // throw new Error('You do not have any permission.')
97
98
  }
98
- const scopes = user.permission.getScopes(required || {})
99
+ const scopes = user.permission.getScopes(required || {}) || []
99
100
  if (!scopes || scopes.length === 0) {
100
- throw new Error('You are not allowed in this scope.')
101
+ // throw new Error('You are not allowed in this scope.')
101
102
  }
102
103
  if (!scopes.includes('*')) {
103
104
  query.tenantCode = user.tenantCode
@@ -105,8 +106,21 @@ function authorize({ allowOwner, query = {}, required, user }) {
105
106
  if (!scopes.includes('TENANT')) {
106
107
  query.eventShortCode = user.eventShortCode
107
108
  }
108
- if (!scopes.includes('EVENT')) {
109
- query.eventRegistrationCode = user.eventRegistrationCode
109
+ // if (!scopes.includes('EVENT')) {
110
+ // query.eventRegistrationCode = user.eventRegistrationCode
111
+ // }
112
+ if (allowCoordinator) {
113
+ if (query.registrationGroupCode && user.myManagedRegistrationGroupCodes.includes(query.registrationGroupCode)) {
114
+ query.__ALLOW_COORDINATOR = true
115
+ } else {
116
+ if (!scopes.includes('EVENT')) {
117
+ query.eventRegistrationCode = user.eventRegistrationCode
118
+ }
119
+ }
120
+ } else {
121
+ if (!scopes.includes('EVENT')) {
122
+ query.eventRegistrationCode = user.eventRegistrationCode
123
+ }
110
124
  }
111
125
  if (allowOwner) {
112
126
  query.__ALLOW_OWNER = true
@@ -1486,7 +1500,7 @@ function convertString(string, patternMatch = /\$\{(.+?)\}/g, value, getValueByK
1486
1500
  if (!string) {
1487
1501
  return ''
1488
1502
  }
1489
- let _getValueByKeys = typeof getValueByKeys !== 'function' ? getValueByKeys : getValueByKeys_getValueByKeys
1503
+ let _getValueByKeys = typeof getValueByKeys === 'function' ? getValueByKeys : getValueByKeys_getValueByKeys
1490
1504
  const reg = new RegExp(patternMatch, 'g')
1491
1505
  return string.replace(reg, (match, key) => {
1492
1506
  const result = _getValueByKeys({ keys: key.split('.'), obj: value })
@@ -1507,7 +1521,7 @@ function convertString(string, patternMatch = /\$\{(.+?)\}/g, value, getValueByK
1507
1521
 
1508
1522
  ;// ./lib/helpers/escapeRegex/escapeRegex.js
1509
1523
  function escapeRegex(string) {
1510
- return string.replace(/[-\/\\^$*+?.()|[\]{}]/g, '\\$&')
1524
+ return String(string).replace(/[-\/\\^$*+?.()|[\]{}]/g, '\\$&')
1511
1525
  }
1512
1526
 
1513
1527
  ;// ./lib/helpers/escapeRegex/index.js
@@ -1943,19 +1957,19 @@ class Repo {
1943
1957
  })
1944
1958
  }
1945
1959
 
1946
- saveAll({ docs, systemLog }) {
1960
+ saveAll({ config = {}, docs, systemLog }) {
1947
1961
  let isNew
1948
1962
  const log = _makeLog({
1949
1963
  systemLog,
1950
1964
  label: 'REPO_WRITE',
1951
1965
  message: `fn ${this._classname}.prototype.saveAll`,
1952
- input: [{ docs: [...docs], systemLog: { ...systemLog } }]
1966
+ input: [{ config, docs: [...docs], systemLog: { ...systemLog } }]
1953
1967
  })
1954
1968
  const promise = typeof this.model.saveAll === 'function'
1955
- ? this.model.saveAll({ docs })
1969
+ ? this.model.saveAll({ config, docs })
1956
1970
  : Promise.all(docs.map(async (doc) => {
1957
1971
  if (doc) {
1958
- const result = await this.saveOne({ doc })
1972
+ const result = await this.saveOne({ config, doc })
1959
1973
  isNew = result.isNew
1960
1974
  const _data = result._data || result.data
1961
1975
  return _data[0]
@@ -1977,15 +1991,19 @@ class Repo {
1977
1991
  })
1978
1992
  }
1979
1993
 
1980
- saveOne({ doc, systemLog }) {
1994
+ saveOne({ config = {}, doc, systemLog }) {
1981
1995
  const log = _makeLog({
1982
1996
  systemLog,
1983
1997
  label: 'REPO_WRITE',
1984
1998
  message: `fn ${this._classname}.prototype.saveOne`,
1985
- input: [{ doc: { ...doc }, systemLog: { ...systemLog } }]
1999
+ input: [{ config, doc: { ...doc }, systemLog: { ...systemLog } }]
1986
2000
  })
2001
+ const saveOptions = {
2002
+ ...this.saveOptions,
2003
+ ...config,
2004
+ }
1987
2005
  return new Promise((resolve, reject) => {
1988
- this.model.saveOne(doc, this.saveOptions, (err, result) => {
2006
+ this.model.saveOne(doc, saveOptions, (err, result) => {
1989
2007
  if (err) {
1990
2008
  log({ level: 'warn', output: err.toString() })
1991
2009
  reject(err)
@@ -2127,11 +2145,11 @@ class Service {
2127
2145
  return this.initFromArray(arr).filter((i) => i)
2128
2146
  }
2129
2147
 
2130
- async saveAll({ docs = [], config = {}, systemLog } = {}) {
2148
+ async saveAll({ config = {}, docs = [], systemLog } = {}) {
2131
2149
  const copies = docs.map((doc) => {
2132
2150
  return config.skipInit ? doc : this.init(doc)
2133
2151
  })
2134
- const result = await this.repo.saveAll({ docs: copies, systemLog })
2152
+ const result = await this.repo.saveAll({ config, docs: copies, systemLog })
2135
2153
  return makeApiResponse({
2136
2154
  repo: this.repo,
2137
2155
  result
@@ -2139,10 +2157,10 @@ class Service {
2139
2157
  }
2140
2158
 
2141
2159
  // set skipInit to true if we want to use POST for query
2142
- async saveOne({ doc = {}, config = {}, systemLog } = {}) {
2160
+ async saveOne({ config = {}, doc = {}, systemLog } = {}) {
2143
2161
  const copy = config.skipInit ? doc : this.init(doc)
2144
2162
  if (copy) {
2145
- const result = await this.repo.saveOne({ doc: copy, systemLog })
2163
+ const result = await this.repo.saveOne({ config, doc: copy, systemLog })
2146
2164
  return makeApiResponse({
2147
2165
  repo: this.repo,
2148
2166
  result
@@ -2612,6 +2630,19 @@ function trackingPlugin(schema, options) {
2612
2630
  next()
2613
2631
  })
2614
2632
 
2633
+ // Add core indexes
2634
+ schema.index({
2635
+ 'meta.active': 1,
2636
+ 'meta.deleted': 1
2637
+ }, {
2638
+ name: 'tracking_status_index',
2639
+ background: true,
2640
+ partialFilterExpression: {
2641
+ 'meta.active': true,
2642
+ 'meta.deleted': false
2643
+ }
2644
+ })
2645
+
2615
2646
  // Optional: Add helper methods
2616
2647
  // schema.methods.touch = function(userId) {
2617
2648
  // this.meta.updatedAt = new Date()
@@ -2619,6 +2650,57 @@ function trackingPlugin(schema, options) {
2619
2650
  // }
2620
2651
  }
2621
2652
 
2653
+ ;// ./lib/helpers/tenantPlugin/tenantPlugin.js
2654
+
2655
+
2656
+ function tenantPlugin(schema, options) {
2657
+ // Apply tracking plugin first if not already present
2658
+ if (!schema.path('meta')) {
2659
+ trackingPlugin(schema, options)
2660
+ }
2661
+
2662
+ // Add tenant-specific fields
2663
+ schema.add({
2664
+ metadata: [{ type: Object }], // Instead of Schema.Types.Mixed
2665
+ remarks: [{ type: Object }],
2666
+ tenantCode: { type: String, required: true }
2667
+ })
2668
+
2669
+ // Add core indexes
2670
+ schema.index({
2671
+ 'tenantCode': 1
2672
+ }, {
2673
+ name: 'tenant_core_index',
2674
+ background: true
2675
+ })
2676
+
2677
+ // 1. ENHANCE EXISTING TRACKING INDEXES
2678
+ const existingIndexes = schema.indexes()
2679
+
2680
+ // Check if tracking_status_index exists
2681
+ const hasTenantStatusIndex = existingIndexes.some(idx =>
2682
+ idx.name === 'tenant_status_index' // Check by name for reliability
2683
+ )
2684
+
2685
+ if (!hasTenantStatusIndex) {
2686
+ schema.index({
2687
+ 'tenantCode': 1, // Unique field first
2688
+ _type: 1, // Low-cardinality field last
2689
+ }, {
2690
+ name: 'tenant_status_index',
2691
+ background: true,
2692
+ partialFilterExpression: {
2693
+ '_type': 'Tenant',
2694
+ 'meta.active': true,
2695
+ 'meta.deleted': false
2696
+ }
2697
+ })
2698
+ }
2699
+ }
2700
+
2701
+ ;// ./lib/helpers/tenantPlugin/index.js
2702
+
2703
+
2622
2704
  ;// ./lib/helpers/trackingPlugin/index.js
2623
2705
 
2624
2706
 
@@ -2645,6 +2727,7 @@ function trackingPlugin(schema, options) {
2645
2727
 
2646
2728
 
2647
2729
 
2730
+
2648
2731
 
2649
2732
 
2650
2733
  ;// ./lib/models/apiResponse/index.js
@@ -2986,7 +3069,10 @@ class KeyValueObject {
2986
3069
  }, [])
2987
3070
  }
2988
3071
  static sameKey(item, key) {
2989
- return _isSame(item.key, key)
3072
+ if (item) {
3073
+ return _isSame(item.key, key)
3074
+ }
3075
+ return false
2990
3076
  }
2991
3077
  static toObject(arr = []) {
2992
3078
  if (Array.isArray(arr)) {
@@ -3240,17 +3326,19 @@ class TrackedEntity {
3240
3326
  constructor(options = {}) {
3241
3327
  options = options || {}
3242
3328
  const timestamp = Date.now()
3243
- const _tracking = {
3244
- active: options.active ?? true,
3245
- created: options.created ?? timestamp,
3246
- creator: options.creator ?? '',
3247
- deleted: options.deleted ?? false,
3248
- modified: options.modified ?? timestamp,
3249
- owner: options.owner ?? '',
3329
+ this.meta = {
3330
+ active: options.meta?.active ?? options.active ?? true,
3331
+ created: options.meta?.created ?? (options.created
3332
+ ? new Date(options.created).getTime()
3333
+ : timestamp),
3334
+ creator: options.meta?.creator ?? options.creator ?? '',
3335
+ deleted: options.meta?.deleted ?? options.deleted ?? false,
3336
+ modified: options.meta?.modified ?? (options.modified
3337
+ ? new Date(options.modified).getTime()
3338
+ : timestamp),
3339
+ owner: options.meta?.owner ?? options.owner ?? '',
3250
3340
  }
3251
3341
 
3252
- this.meta = { ..._tracking, ...options.meta }
3253
-
3254
3342
  // if (trackFlat) {
3255
3343
  // Object.assign(this, _tracking)
3256
3344
  // } else {
@@ -1,15 +1,15 @@
1
1
 
2
2
  ;// ./lib/helpers/authorize/authorize.js
3
- function authorize({ allowOwner, query = {}, required, user }) {
3
+ function authorize({ allowCoordinator, allowOwner, query = {}, required, user }) {
4
4
  if (!user) {
5
5
  throw new Error('Require login.')
6
6
  }
7
7
  if (!user.permission) {
8
- throw new Error('You do not have any permission.')
8
+ // throw new Error('You do not have any permission.')
9
9
  }
10
- const scopes = user.permission.getScopes(required || {})
10
+ const scopes = user.permission.getScopes(required || {}) || []
11
11
  if (!scopes || scopes.length === 0) {
12
- throw new Error('You are not allowed in this scope.')
12
+ // throw new Error('You are not allowed in this scope.')
13
13
  }
14
14
  if (!scopes.includes('*')) {
15
15
  query.tenantCode = user.tenantCode
@@ -17,8 +17,21 @@ function authorize({ allowOwner, query = {}, required, user }) {
17
17
  if (!scopes.includes('TENANT')) {
18
18
  query.eventShortCode = user.eventShortCode
19
19
  }
20
- if (!scopes.includes('EVENT')) {
21
- query.eventRegistrationCode = user.eventRegistrationCode
20
+ // if (!scopes.includes('EVENT')) {
21
+ // query.eventRegistrationCode = user.eventRegistrationCode
22
+ // }
23
+ if (allowCoordinator) {
24
+ if (query.registrationGroupCode && user.myManagedRegistrationGroupCodes.includes(query.registrationGroupCode)) {
25
+ query.__ALLOW_COORDINATOR = true
26
+ } else {
27
+ if (!scopes.includes('EVENT')) {
28
+ query.eventRegistrationCode = user.eventRegistrationCode
29
+ }
30
+ }
31
+ } else {
32
+ if (!scopes.includes('EVENT')) {
33
+ query.eventRegistrationCode = user.eventRegistrationCode
34
+ }
22
35
  }
23
36
  if (allowOwner) {
24
37
  query.__ALLOW_OWNER = true
@@ -1398,7 +1411,7 @@ function convertString(string, patternMatch = /\$\{(.+?)\}/g, value, getValueByK
1398
1411
  if (!string) {
1399
1412
  return ''
1400
1413
  }
1401
- let _getValueByKeys = typeof getValueByKeys !== 'function' ? getValueByKeys : getValueByKeys_getValueByKeys
1414
+ let _getValueByKeys = typeof getValueByKeys === 'function' ? getValueByKeys : getValueByKeys_getValueByKeys
1402
1415
  const reg = new RegExp(patternMatch, 'g')
1403
1416
  return string.replace(reg, (match, key) => {
1404
1417
  const result = _getValueByKeys({ keys: key.split('.'), obj: value })
@@ -1419,7 +1432,7 @@ function convertString(string, patternMatch = /\$\{(.+?)\}/g, value, getValueByK
1419
1432
 
1420
1433
  ;// ./lib/helpers/escapeRegex/escapeRegex.js
1421
1434
  function escapeRegex(string) {
1422
- return string.replace(/[-\/\\^$*+?.()|[\]{}]/g, '\\$&')
1435
+ return String(string).replace(/[-\/\\^$*+?.()|[\]{}]/g, '\\$&')
1423
1436
  }
1424
1437
 
1425
1438
  ;// ./lib/helpers/escapeRegex/index.js
@@ -1855,19 +1868,19 @@ class Repo {
1855
1868
  })
1856
1869
  }
1857
1870
 
1858
- saveAll({ docs, systemLog }) {
1871
+ saveAll({ config = {}, docs, systemLog }) {
1859
1872
  let isNew
1860
1873
  const log = _makeLog({
1861
1874
  systemLog,
1862
1875
  label: 'REPO_WRITE',
1863
1876
  message: `fn ${this._classname}.prototype.saveAll`,
1864
- input: [{ docs: [...docs], systemLog: { ...systemLog } }]
1877
+ input: [{ config, docs: [...docs], systemLog: { ...systemLog } }]
1865
1878
  })
1866
1879
  const promise = typeof this.model.saveAll === 'function'
1867
- ? this.model.saveAll({ docs })
1880
+ ? this.model.saveAll({ config, docs })
1868
1881
  : Promise.all(docs.map(async (doc) => {
1869
1882
  if (doc) {
1870
- const result = await this.saveOne({ doc })
1883
+ const result = await this.saveOne({ config, doc })
1871
1884
  isNew = result.isNew
1872
1885
  const _data = result._data || result.data
1873
1886
  return _data[0]
@@ -1889,15 +1902,19 @@ class Repo {
1889
1902
  })
1890
1903
  }
1891
1904
 
1892
- saveOne({ doc, systemLog }) {
1905
+ saveOne({ config = {}, doc, systemLog }) {
1893
1906
  const log = _makeLog({
1894
1907
  systemLog,
1895
1908
  label: 'REPO_WRITE',
1896
1909
  message: `fn ${this._classname}.prototype.saveOne`,
1897
- input: [{ doc: { ...doc }, systemLog: { ...systemLog } }]
1910
+ input: [{ config, doc: { ...doc }, systemLog: { ...systemLog } }]
1898
1911
  })
1912
+ const saveOptions = {
1913
+ ...this.saveOptions,
1914
+ ...config,
1915
+ }
1899
1916
  return new Promise((resolve, reject) => {
1900
- this.model.saveOne(doc, this.saveOptions, (err, result) => {
1917
+ this.model.saveOne(doc, saveOptions, (err, result) => {
1901
1918
  if (err) {
1902
1919
  log({ level: 'warn', output: err.toString() })
1903
1920
  reject(err)
@@ -2039,11 +2056,11 @@ class Service {
2039
2056
  return this.initFromArray(arr).filter((i) => i)
2040
2057
  }
2041
2058
 
2042
- async saveAll({ docs = [], config = {}, systemLog } = {}) {
2059
+ async saveAll({ config = {}, docs = [], systemLog } = {}) {
2043
2060
  const copies = docs.map((doc) => {
2044
2061
  return config.skipInit ? doc : this.init(doc)
2045
2062
  })
2046
- const result = await this.repo.saveAll({ docs: copies, systemLog })
2063
+ const result = await this.repo.saveAll({ config, docs: copies, systemLog })
2047
2064
  return makeApiResponse({
2048
2065
  repo: this.repo,
2049
2066
  result
@@ -2051,10 +2068,10 @@ class Service {
2051
2068
  }
2052
2069
 
2053
2070
  // set skipInit to true if we want to use POST for query
2054
- async saveOne({ doc = {}, config = {}, systemLog } = {}) {
2071
+ async saveOne({ config = {}, doc = {}, systemLog } = {}) {
2055
2072
  const copy = config.skipInit ? doc : this.init(doc)
2056
2073
  if (copy) {
2057
- const result = await this.repo.saveOne({ doc: copy, systemLog })
2074
+ const result = await this.repo.saveOne({ config, doc: copy, systemLog })
2058
2075
  return makeApiResponse({
2059
2076
  repo: this.repo,
2060
2077
  result
@@ -2524,6 +2541,19 @@ function trackingPlugin(schema, options) {
2524
2541
  next()
2525
2542
  })
2526
2543
 
2544
+ // Add core indexes
2545
+ schema.index({
2546
+ 'meta.active': 1,
2547
+ 'meta.deleted': 1
2548
+ }, {
2549
+ name: 'tracking_status_index',
2550
+ background: true,
2551
+ partialFilterExpression: {
2552
+ 'meta.active': true,
2553
+ 'meta.deleted': false
2554
+ }
2555
+ })
2556
+
2527
2557
  // Optional: Add helper methods
2528
2558
  // schema.methods.touch = function(userId) {
2529
2559
  // this.meta.updatedAt = new Date()
@@ -2531,6 +2561,57 @@ function trackingPlugin(schema, options) {
2531
2561
  // }
2532
2562
  }
2533
2563
 
2564
+ ;// ./lib/helpers/tenantPlugin/tenantPlugin.js
2565
+
2566
+
2567
+ function tenantPlugin(schema, options) {
2568
+ // Apply tracking plugin first if not already present
2569
+ if (!schema.path('meta')) {
2570
+ trackingPlugin(schema, options)
2571
+ }
2572
+
2573
+ // Add tenant-specific fields
2574
+ schema.add({
2575
+ metadata: [{ type: Object }], // Instead of Schema.Types.Mixed
2576
+ remarks: [{ type: Object }],
2577
+ tenantCode: { type: String, required: true }
2578
+ })
2579
+
2580
+ // Add core indexes
2581
+ schema.index({
2582
+ 'tenantCode': 1
2583
+ }, {
2584
+ name: 'tenant_core_index',
2585
+ background: true
2586
+ })
2587
+
2588
+ // 1. ENHANCE EXISTING TRACKING INDEXES
2589
+ const existingIndexes = schema.indexes()
2590
+
2591
+ // Check if tracking_status_index exists
2592
+ const hasTenantStatusIndex = existingIndexes.some(idx =>
2593
+ idx.name === 'tenant_status_index' // Check by name for reliability
2594
+ )
2595
+
2596
+ if (!hasTenantStatusIndex) {
2597
+ schema.index({
2598
+ 'tenantCode': 1, // Unique field first
2599
+ _type: 1, // Low-cardinality field last
2600
+ }, {
2601
+ name: 'tenant_status_index',
2602
+ background: true,
2603
+ partialFilterExpression: {
2604
+ '_type': 'Tenant',
2605
+ 'meta.active': true,
2606
+ 'meta.deleted': false
2607
+ }
2608
+ })
2609
+ }
2610
+ }
2611
+
2612
+ ;// ./lib/helpers/tenantPlugin/index.js
2613
+
2614
+
2534
2615
  ;// ./lib/helpers/trackingPlugin/index.js
2535
2616
 
2536
2617
 
@@ -2557,6 +2638,7 @@ function trackingPlugin(schema, options) {
2557
2638
 
2558
2639
 
2559
2640
 
2641
+
2560
2642
 
2561
2643
 
2562
2644
  ;// ./lib/models/apiResponse/index.js
@@ -2898,7 +2980,10 @@ class KeyValueObject {
2898
2980
  }, [])
2899
2981
  }
2900
2982
  static sameKey(item, key) {
2901
- return _isSame(item.key, key)
2983
+ if (item) {
2984
+ return _isSame(item.key, key)
2985
+ }
2986
+ return false
2902
2987
  }
2903
2988
  static toObject(arr = []) {
2904
2989
  if (Array.isArray(arr)) {
@@ -3152,17 +3237,19 @@ class TrackedEntity {
3152
3237
  constructor(options = {}) {
3153
3238
  options = options || {}
3154
3239
  const timestamp = Date.now()
3155
- const _tracking = {
3156
- active: options.active ?? true,
3157
- created: options.created ?? timestamp,
3158
- creator: options.creator ?? '',
3159
- deleted: options.deleted ?? false,
3160
- modified: options.modified ?? timestamp,
3161
- owner: options.owner ?? '',
3240
+ this.meta = {
3241
+ active: options.meta?.active ?? options.active ?? true,
3242
+ created: options.meta?.created ?? (options.created
3243
+ ? new Date(options.created).getTime()
3244
+ : timestamp),
3245
+ creator: options.meta?.creator ?? options.creator ?? '',
3246
+ deleted: options.meta?.deleted ?? options.deleted ?? false,
3247
+ modified: options.meta?.modified ?? (options.modified
3248
+ ? new Date(options.modified).getTime()
3249
+ : timestamp),
3250
+ owner: options.meta?.owner ?? options.owner ?? '',
3162
3251
  }
3163
3252
 
3164
- this.meta = { ..._tracking, ...options.meta }
3165
-
3166
3253
  // if (trackFlat) {
3167
3254
  // Object.assign(this, _tracking)
3168
3255
  // } else {
@@ -3431,4 +3518,4 @@ function _makeSetCode(fieldName, options) {
3431
3518
  ;// ./index.js
3432
3519
 
3433
3520
 
3434
- export { ApiResponse, AwsStsS3Client, KeyValueObject, Metadata, QMeta, Repo, Service, TemplateCompiler, TenantAwareEntity, TrackedEntity, UniqueKeyGenerator, authorize, changeCreatorOwner, concatStringByArray, convertString, escapeRegex, expressHelper, extractEmails, formatDate, generalPost, getValidation, getValueByKeys_getValueByKeys as getValueByKeys, init, initFromArray, initOnlyValidFromArray, makeApiResponse, makeService, mergeArraysByKey, objectHelper, pReduce, padZeros, replacePlaceholders, sanitizeText, stringFormatter, stringHelper, trackingPlugin };
3521
+ export { ApiResponse, AwsStsS3Client, KeyValueObject, Metadata, QMeta, Repo, Service, TemplateCompiler, TenantAwareEntity, TrackedEntity, UniqueKeyGenerator, authorize, changeCreatorOwner, concatStringByArray, convertString, escapeRegex, expressHelper, extractEmails, formatDate, generalPost, getValidation, getValueByKeys_getValueByKeys as getValueByKeys, init, initFromArray, initOnlyValidFromArray, makeApiResponse, makeService, mergeArraysByKey, objectHelper, pReduce, padZeros, replacePlaceholders, sanitizeText, stringFormatter, stringHelper, tenantPlugin, trackingPlugin };
@@ -83,20 +83,21 @@ __webpack_require__.d(__webpack_exports__, {
83
83
  sanitizeText: () => (/* reexport */ sanitizeText),
84
84
  stringFormatter: () => (/* reexport */ stringFormatter),
85
85
  stringHelper: () => (/* reexport */ stringHelper),
86
+ tenantPlugin: () => (/* reexport */ tenantPlugin),
86
87
  trackingPlugin: () => (/* reexport */ trackingPlugin)
87
88
  });
88
89
 
89
90
  ;// ./lib/helpers/authorize/authorize.js
90
- function authorize({ allowOwner, query = {}, required, user }) {
91
+ function authorize({ allowCoordinator, allowOwner, query = {}, required, user }) {
91
92
  if (!user) {
92
93
  throw new Error('Require login.')
93
94
  }
94
95
  if (!user.permission) {
95
- throw new Error('You do not have any permission.')
96
+ // throw new Error('You do not have any permission.')
96
97
  }
97
- const scopes = user.permission.getScopes(required || {})
98
+ const scopes = user.permission.getScopes(required || {}) || []
98
99
  if (!scopes || scopes.length === 0) {
99
- throw new Error('You are not allowed in this scope.')
100
+ // throw new Error('You are not allowed in this scope.')
100
101
  }
101
102
  if (!scopes.includes('*')) {
102
103
  query.tenantCode = user.tenantCode
@@ -104,8 +105,21 @@ function authorize({ allowOwner, query = {}, required, user }) {
104
105
  if (!scopes.includes('TENANT')) {
105
106
  query.eventShortCode = user.eventShortCode
106
107
  }
107
- if (!scopes.includes('EVENT')) {
108
- query.eventRegistrationCode = user.eventRegistrationCode
108
+ // if (!scopes.includes('EVENT')) {
109
+ // query.eventRegistrationCode = user.eventRegistrationCode
110
+ // }
111
+ if (allowCoordinator) {
112
+ if (query.registrationGroupCode && user.myManagedRegistrationGroupCodes.includes(query.registrationGroupCode)) {
113
+ query.__ALLOW_COORDINATOR = true
114
+ } else {
115
+ if (!scopes.includes('EVENT')) {
116
+ query.eventRegistrationCode = user.eventRegistrationCode
117
+ }
118
+ }
119
+ } else {
120
+ if (!scopes.includes('EVENT')) {
121
+ query.eventRegistrationCode = user.eventRegistrationCode
122
+ }
109
123
  }
110
124
  if (allowOwner) {
111
125
  query.__ALLOW_OWNER = true
@@ -1485,7 +1499,7 @@ function convertString(string, patternMatch = /\$\{(.+?)\}/g, value, getValueByK
1485
1499
  if (!string) {
1486
1500
  return ''
1487
1501
  }
1488
- let _getValueByKeys = typeof getValueByKeys !== 'function' ? getValueByKeys : getValueByKeys_getValueByKeys
1502
+ let _getValueByKeys = typeof getValueByKeys === 'function' ? getValueByKeys : getValueByKeys_getValueByKeys
1489
1503
  const reg = new RegExp(patternMatch, 'g')
1490
1504
  return string.replace(reg, (match, key) => {
1491
1505
  const result = _getValueByKeys({ keys: key.split('.'), obj: value })
@@ -1506,7 +1520,7 @@ function convertString(string, patternMatch = /\$\{(.+?)\}/g, value, getValueByK
1506
1520
 
1507
1521
  ;// ./lib/helpers/escapeRegex/escapeRegex.js
1508
1522
  function escapeRegex(string) {
1509
- return string.replace(/[-\/\\^$*+?.()|[\]{}]/g, '\\$&')
1523
+ return String(string).replace(/[-\/\\^$*+?.()|[\]{}]/g, '\\$&')
1510
1524
  }
1511
1525
 
1512
1526
  ;// ./lib/helpers/escapeRegex/index.js
@@ -1942,19 +1956,19 @@ class Repo {
1942
1956
  })
1943
1957
  }
1944
1958
 
1945
- saveAll({ docs, systemLog }) {
1959
+ saveAll({ config = {}, docs, systemLog }) {
1946
1960
  let isNew
1947
1961
  const log = _makeLog({
1948
1962
  systemLog,
1949
1963
  label: 'REPO_WRITE',
1950
1964
  message: `fn ${this._classname}.prototype.saveAll`,
1951
- input: [{ docs: [...docs], systemLog: { ...systemLog } }]
1965
+ input: [{ config, docs: [...docs], systemLog: { ...systemLog } }]
1952
1966
  })
1953
1967
  const promise = typeof this.model.saveAll === 'function'
1954
- ? this.model.saveAll({ docs })
1968
+ ? this.model.saveAll({ config, docs })
1955
1969
  : Promise.all(docs.map(async (doc) => {
1956
1970
  if (doc) {
1957
- const result = await this.saveOne({ doc })
1971
+ const result = await this.saveOne({ config, doc })
1958
1972
  isNew = result.isNew
1959
1973
  const _data = result._data || result.data
1960
1974
  return _data[0]
@@ -1976,15 +1990,19 @@ class Repo {
1976
1990
  })
1977
1991
  }
1978
1992
 
1979
- saveOne({ doc, systemLog }) {
1993
+ saveOne({ config = {}, doc, systemLog }) {
1980
1994
  const log = _makeLog({
1981
1995
  systemLog,
1982
1996
  label: 'REPO_WRITE',
1983
1997
  message: `fn ${this._classname}.prototype.saveOne`,
1984
- input: [{ doc: { ...doc }, systemLog: { ...systemLog } }]
1998
+ input: [{ config, doc: { ...doc }, systemLog: { ...systemLog } }]
1985
1999
  })
2000
+ const saveOptions = {
2001
+ ...this.saveOptions,
2002
+ ...config,
2003
+ }
1986
2004
  return new Promise((resolve, reject) => {
1987
- this.model.saveOne(doc, this.saveOptions, (err, result) => {
2005
+ this.model.saveOne(doc, saveOptions, (err, result) => {
1988
2006
  if (err) {
1989
2007
  log({ level: 'warn', output: err.toString() })
1990
2008
  reject(err)
@@ -2126,11 +2144,11 @@ class Service {
2126
2144
  return this.initFromArray(arr).filter((i) => i)
2127
2145
  }
2128
2146
 
2129
- async saveAll({ docs = [], config = {}, systemLog } = {}) {
2147
+ async saveAll({ config = {}, docs = [], systemLog } = {}) {
2130
2148
  const copies = docs.map((doc) => {
2131
2149
  return config.skipInit ? doc : this.init(doc)
2132
2150
  })
2133
- const result = await this.repo.saveAll({ docs: copies, systemLog })
2151
+ const result = await this.repo.saveAll({ config, docs: copies, systemLog })
2134
2152
  return makeApiResponse({
2135
2153
  repo: this.repo,
2136
2154
  result
@@ -2138,10 +2156,10 @@ class Service {
2138
2156
  }
2139
2157
 
2140
2158
  // set skipInit to true if we want to use POST for query
2141
- async saveOne({ doc = {}, config = {}, systemLog } = {}) {
2159
+ async saveOne({ config = {}, doc = {}, systemLog } = {}) {
2142
2160
  const copy = config.skipInit ? doc : this.init(doc)
2143
2161
  if (copy) {
2144
- const result = await this.repo.saveOne({ doc: copy, systemLog })
2162
+ const result = await this.repo.saveOne({ config, doc: copy, systemLog })
2145
2163
  return makeApiResponse({
2146
2164
  repo: this.repo,
2147
2165
  result
@@ -2611,6 +2629,19 @@ function trackingPlugin(schema, options) {
2611
2629
  next()
2612
2630
  })
2613
2631
 
2632
+ // Add core indexes
2633
+ schema.index({
2634
+ 'meta.active': 1,
2635
+ 'meta.deleted': 1
2636
+ }, {
2637
+ name: 'tracking_status_index',
2638
+ background: true,
2639
+ partialFilterExpression: {
2640
+ 'meta.active': true,
2641
+ 'meta.deleted': false
2642
+ }
2643
+ })
2644
+
2614
2645
  // Optional: Add helper methods
2615
2646
  // schema.methods.touch = function(userId) {
2616
2647
  // this.meta.updatedAt = new Date()
@@ -2618,6 +2649,57 @@ function trackingPlugin(schema, options) {
2618
2649
  // }
2619
2650
  }
2620
2651
 
2652
+ ;// ./lib/helpers/tenantPlugin/tenantPlugin.js
2653
+
2654
+
2655
+ function tenantPlugin(schema, options) {
2656
+ // Apply tracking plugin first if not already present
2657
+ if (!schema.path('meta')) {
2658
+ trackingPlugin(schema, options)
2659
+ }
2660
+
2661
+ // Add tenant-specific fields
2662
+ schema.add({
2663
+ metadata: [{ type: Object }], // Instead of Schema.Types.Mixed
2664
+ remarks: [{ type: Object }],
2665
+ tenantCode: { type: String, required: true }
2666
+ })
2667
+
2668
+ // Add core indexes
2669
+ schema.index({
2670
+ 'tenantCode': 1
2671
+ }, {
2672
+ name: 'tenant_core_index',
2673
+ background: true
2674
+ })
2675
+
2676
+ // 1. ENHANCE EXISTING TRACKING INDEXES
2677
+ const existingIndexes = schema.indexes()
2678
+
2679
+ // Check if tracking_status_index exists
2680
+ const hasTenantStatusIndex = existingIndexes.some(idx =>
2681
+ idx.name === 'tenant_status_index' // Check by name for reliability
2682
+ )
2683
+
2684
+ if (!hasTenantStatusIndex) {
2685
+ schema.index({
2686
+ 'tenantCode': 1, // Unique field first
2687
+ _type: 1, // Low-cardinality field last
2688
+ }, {
2689
+ name: 'tenant_status_index',
2690
+ background: true,
2691
+ partialFilterExpression: {
2692
+ '_type': 'Tenant',
2693
+ 'meta.active': true,
2694
+ 'meta.deleted': false
2695
+ }
2696
+ })
2697
+ }
2698
+ }
2699
+
2700
+ ;// ./lib/helpers/tenantPlugin/index.js
2701
+
2702
+
2621
2703
  ;// ./lib/helpers/trackingPlugin/index.js
2622
2704
 
2623
2705
 
@@ -2644,6 +2726,7 @@ function trackingPlugin(schema, options) {
2644
2726
 
2645
2727
 
2646
2728
 
2729
+
2647
2730
 
2648
2731
 
2649
2732
  ;// ./lib/models/apiResponse/index.js
@@ -2985,7 +3068,10 @@ class KeyValueObject {
2985
3068
  }, [])
2986
3069
  }
2987
3070
  static sameKey(item, key) {
2988
- return _isSame(item.key, key)
3071
+ if (item) {
3072
+ return _isSame(item.key, key)
3073
+ }
3074
+ return false
2989
3075
  }
2990
3076
  static toObject(arr = []) {
2991
3077
  if (Array.isArray(arr)) {
@@ -3239,17 +3325,19 @@ class TrackedEntity {
3239
3325
  constructor(options = {}) {
3240
3326
  options = options || {}
3241
3327
  const timestamp = Date.now()
3242
- const _tracking = {
3243
- active: options.active ?? true,
3244
- created: options.created ?? timestamp,
3245
- creator: options.creator ?? '',
3246
- deleted: options.deleted ?? false,
3247
- modified: options.modified ?? timestamp,
3248
- owner: options.owner ?? '',
3328
+ this.meta = {
3329
+ active: options.meta?.active ?? options.active ?? true,
3330
+ created: options.meta?.created ?? (options.created
3331
+ ? new Date(options.created).getTime()
3332
+ : timestamp),
3333
+ creator: options.meta?.creator ?? options.creator ?? '',
3334
+ deleted: options.meta?.deleted ?? options.deleted ?? false,
3335
+ modified: options.meta?.modified ?? (options.modified
3336
+ ? new Date(options.modified).getTime()
3337
+ : timestamp),
3338
+ owner: options.meta?.owner ?? options.owner ?? '',
3249
3339
  }
3250
3340
 
3251
- this.meta = { ..._tracking, ...options.meta }
3252
-
3253
3341
  // if (trackFlat) {
3254
3342
  // Object.assign(this, _tracking)
3255
3343
  // } else {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@questwork/q-utilities",
3
- "version": "0.1.15",
3
+ "version": "0.1.16",
4
4
  "description": "Questwork QUtilities",
5
5
  "type": "module",
6
6
  "exports": {