@questwork/q-utilities 0.1.15 → 0.1.17
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/dist/index.min.cjs +151 -47
- package/dist/q-utilities.esm.js +151 -48
- package/dist/q-utilities.min.js +151 -47
- package/package.json +8 -8
package/dist/index.min.cjs
CHANGED
|
@@ -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
|
-
|
|
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
|
|
@@ -365,6 +379,9 @@ function getValueByKeys_getValueByKeys(keys, data) {
|
|
|
365
379
|
return getValueByKeys_getValueByKeys(_keys, _data[firstKey])
|
|
366
380
|
}
|
|
367
381
|
if (_data && firstKey) {
|
|
382
|
+
if (_keys.length > 0) {
|
|
383
|
+
return getValueByKeys_getValueByKeys(_keys, _data[firstKey])
|
|
384
|
+
}
|
|
368
385
|
return _data[firstKey]
|
|
369
386
|
}
|
|
370
387
|
return _data
|
|
@@ -1418,19 +1435,6 @@ function concatStringByArray(arrTemplate, data) {
|
|
|
1418
1435
|
return arrTemplate.reduce((acc, item) => {
|
|
1419
1436
|
const { type, value = '', restriction, template, format, showMinutes } = item
|
|
1420
1437
|
switch (type) {
|
|
1421
|
-
case('label'): {
|
|
1422
|
-
if (getValidation(restriction, data, getValueByKeys_getValueByKeys)) {
|
|
1423
|
-
acc += (value.toString())
|
|
1424
|
-
}
|
|
1425
|
-
break
|
|
1426
|
-
}
|
|
1427
|
-
case('value'): {
|
|
1428
|
-
if (getValidation(restriction, data, getValueByKeys_getValueByKeys)) {
|
|
1429
|
-
const _value = getValueByKeys_getValueByKeys(value.split('.'), data) || ''
|
|
1430
|
-
acc += (_value.toString())
|
|
1431
|
-
}
|
|
1432
|
-
break
|
|
1433
|
-
}
|
|
1434
1438
|
case('array'): {
|
|
1435
1439
|
if (getValidation(restriction, data, getValueByKeys_getValueByKeys)) {
|
|
1436
1440
|
const _value = getValueByKeys_getValueByKeys(value.split('.'), data) || []
|
|
@@ -1440,6 +1444,13 @@ function concatStringByArray(arrTemplate, data) {
|
|
|
1440
1444
|
}
|
|
1441
1445
|
break
|
|
1442
1446
|
}
|
|
1447
|
+
case ('date'): {
|
|
1448
|
+
if (getValidation(restriction, data, getValueByKeys_getValueByKeys)) {
|
|
1449
|
+
const _value = getValueByKeys_getValueByKeys(value.split('.'), data) || ''
|
|
1450
|
+
acc += (formatDate(_value, format).toString())
|
|
1451
|
+
}
|
|
1452
|
+
break
|
|
1453
|
+
}
|
|
1443
1454
|
case('ellipsis'): {
|
|
1444
1455
|
if (getValidation(restriction, data, getValueByKeys_getValueByKeys)) {
|
|
1445
1456
|
const { maxLength } = item
|
|
@@ -1452,10 +1463,15 @@ function concatStringByArray(arrTemplate, data) {
|
|
|
1452
1463
|
}
|
|
1453
1464
|
break
|
|
1454
1465
|
}
|
|
1455
|
-
case
|
|
1466
|
+
case('group'): {
|
|
1456
1467
|
if (getValidation(restriction, data, getValueByKeys_getValueByKeys)) {
|
|
1457
|
-
|
|
1458
|
-
|
|
1468
|
+
return concatStringByArray(value, data)
|
|
1469
|
+
}
|
|
1470
|
+
break
|
|
1471
|
+
}
|
|
1472
|
+
case('label'): {
|
|
1473
|
+
if (getValidation(restriction, data, getValueByKeys_getValueByKeys)) {
|
|
1474
|
+
acc += (value.toString())
|
|
1459
1475
|
}
|
|
1460
1476
|
break
|
|
1461
1477
|
}
|
|
@@ -1466,6 +1482,13 @@ function concatStringByArray(arrTemplate, data) {
|
|
|
1466
1482
|
}
|
|
1467
1483
|
break
|
|
1468
1484
|
}
|
|
1485
|
+
case('value'): {
|
|
1486
|
+
if (getValidation(restriction, data, getValueByKeys_getValueByKeys)) {
|
|
1487
|
+
const _value = getValueByKeys_getValueByKeys(value.split('.'), data) || ''
|
|
1488
|
+
acc += (_value.toString())
|
|
1489
|
+
}
|
|
1490
|
+
break
|
|
1491
|
+
}
|
|
1469
1492
|
}
|
|
1470
1493
|
return acc
|
|
1471
1494
|
}, '')
|
|
@@ -1486,7 +1509,7 @@ function convertString(string, patternMatch = /\$\{(.+?)\}/g, value, getValueByK
|
|
|
1486
1509
|
if (!string) {
|
|
1487
1510
|
return ''
|
|
1488
1511
|
}
|
|
1489
|
-
let _getValueByKeys = typeof getValueByKeys
|
|
1512
|
+
let _getValueByKeys = typeof getValueByKeys === 'function' ? getValueByKeys : getValueByKeys_getValueByKeys
|
|
1490
1513
|
const reg = new RegExp(patternMatch, 'g')
|
|
1491
1514
|
return string.replace(reg, (match, key) => {
|
|
1492
1515
|
const result = _getValueByKeys({ keys: key.split('.'), obj: value })
|
|
@@ -1507,7 +1530,7 @@ function convertString(string, patternMatch = /\$\{(.+?)\}/g, value, getValueByK
|
|
|
1507
1530
|
|
|
1508
1531
|
;// ./lib/helpers/escapeRegex/escapeRegex.js
|
|
1509
1532
|
function escapeRegex(string) {
|
|
1510
|
-
return string.replace(/[-\/\\^$*+?.()|[\]{}]/g, '\\$&')
|
|
1533
|
+
return String(string).replace(/[-\/\\^$*+?.()|[\]{}]/g, '\\$&')
|
|
1511
1534
|
}
|
|
1512
1535
|
|
|
1513
1536
|
;// ./lib/helpers/escapeRegex/index.js
|
|
@@ -1943,19 +1966,19 @@ class Repo {
|
|
|
1943
1966
|
})
|
|
1944
1967
|
}
|
|
1945
1968
|
|
|
1946
|
-
saveAll({ docs, systemLog }) {
|
|
1969
|
+
saveAll({ config = {}, docs, systemLog }) {
|
|
1947
1970
|
let isNew
|
|
1948
1971
|
const log = _makeLog({
|
|
1949
1972
|
systemLog,
|
|
1950
1973
|
label: 'REPO_WRITE',
|
|
1951
1974
|
message: `fn ${this._classname}.prototype.saveAll`,
|
|
1952
|
-
input: [{ docs: [...docs], systemLog: { ...systemLog } }]
|
|
1975
|
+
input: [{ config, docs: [...docs], systemLog: { ...systemLog } }]
|
|
1953
1976
|
})
|
|
1954
1977
|
const promise = typeof this.model.saveAll === 'function'
|
|
1955
|
-
? this.model.saveAll({ docs })
|
|
1978
|
+
? this.model.saveAll({ config, docs })
|
|
1956
1979
|
: Promise.all(docs.map(async (doc) => {
|
|
1957
1980
|
if (doc) {
|
|
1958
|
-
const result = await this.saveOne({ doc })
|
|
1981
|
+
const result = await this.saveOne({ config, doc })
|
|
1959
1982
|
isNew = result.isNew
|
|
1960
1983
|
const _data = result._data || result.data
|
|
1961
1984
|
return _data[0]
|
|
@@ -1977,15 +2000,19 @@ class Repo {
|
|
|
1977
2000
|
})
|
|
1978
2001
|
}
|
|
1979
2002
|
|
|
1980
|
-
saveOne({ doc, systemLog }) {
|
|
2003
|
+
saveOne({ config = {}, doc, systemLog }) {
|
|
1981
2004
|
const log = _makeLog({
|
|
1982
2005
|
systemLog,
|
|
1983
2006
|
label: 'REPO_WRITE',
|
|
1984
2007
|
message: `fn ${this._classname}.prototype.saveOne`,
|
|
1985
|
-
input: [{ doc: { ...doc }, systemLog: { ...systemLog } }]
|
|
2008
|
+
input: [{ config, doc: { ...doc }, systemLog: { ...systemLog } }]
|
|
1986
2009
|
})
|
|
2010
|
+
const saveOptions = {
|
|
2011
|
+
...this.saveOptions,
|
|
2012
|
+
...config,
|
|
2013
|
+
}
|
|
1987
2014
|
return new Promise((resolve, reject) => {
|
|
1988
|
-
this.model.saveOne(doc,
|
|
2015
|
+
this.model.saveOne(doc, saveOptions, (err, result) => {
|
|
1989
2016
|
if (err) {
|
|
1990
2017
|
log({ level: 'warn', output: err.toString() })
|
|
1991
2018
|
reject(err)
|
|
@@ -2127,11 +2154,11 @@ class Service {
|
|
|
2127
2154
|
return this.initFromArray(arr).filter((i) => i)
|
|
2128
2155
|
}
|
|
2129
2156
|
|
|
2130
|
-
async saveAll({
|
|
2157
|
+
async saveAll({ config = {}, docs = [], systemLog } = {}) {
|
|
2131
2158
|
const copies = docs.map((doc) => {
|
|
2132
2159
|
return config.skipInit ? doc : this.init(doc)
|
|
2133
2160
|
})
|
|
2134
|
-
const result = await this.repo.saveAll({ docs: copies, systemLog })
|
|
2161
|
+
const result = await this.repo.saveAll({ config, docs: copies, systemLog })
|
|
2135
2162
|
return makeApiResponse({
|
|
2136
2163
|
repo: this.repo,
|
|
2137
2164
|
result
|
|
@@ -2139,10 +2166,10 @@ class Service {
|
|
|
2139
2166
|
}
|
|
2140
2167
|
|
|
2141
2168
|
// set skipInit to true if we want to use POST for query
|
|
2142
|
-
async saveOne({
|
|
2169
|
+
async saveOne({ config = {}, doc = {}, systemLog } = {}) {
|
|
2143
2170
|
const copy = config.skipInit ? doc : this.init(doc)
|
|
2144
2171
|
if (copy) {
|
|
2145
|
-
const result = await this.repo.saveOne({ doc: copy, systemLog })
|
|
2172
|
+
const result = await this.repo.saveOne({ config, doc: copy, systemLog })
|
|
2146
2173
|
return makeApiResponse({
|
|
2147
2174
|
repo: this.repo,
|
|
2148
2175
|
result
|
|
@@ -2415,6 +2442,7 @@ function sanitizeText(input, options = {}) {
|
|
|
2415
2442
|
normalizeWhitespace = true,
|
|
2416
2443
|
removeNewlines = false,
|
|
2417
2444
|
trim = true,
|
|
2445
|
+
preserveBasicWhitespace = true, // new option to keep tabs, newlines if removeNewlines=false
|
|
2418
2446
|
} = options
|
|
2419
2447
|
|
|
2420
2448
|
if (typeof input !== 'string') {
|
|
@@ -2423,8 +2451,14 @@ function sanitizeText(input, options = {}) {
|
|
|
2423
2451
|
|
|
2424
2452
|
let result = input
|
|
2425
2453
|
|
|
2426
|
-
// Phase 1: Remove
|
|
2427
|
-
|
|
2454
|
+
// Phase 1: Remove all control characters except basic whitespace if requested
|
|
2455
|
+
if (preserveBasicWhitespace && !removeNewlines) {
|
|
2456
|
+
// Keep tab (\t), newline (\n), carriage return (\r)
|
|
2457
|
+
result = result.replace(/[\x00-\x08\x0B\x0C\x0E-\x1F\x7F-\x9F\u200B-\u200D\uFEFF\u202A-\u202E]/g, '')
|
|
2458
|
+
} else {
|
|
2459
|
+
// Remove all control characters including basic whitespace
|
|
2460
|
+
result = result.replace(/[\x00-\x1F\x7F-\x9F\u200B-\u200D\uFEFF\u202A-\u202E]/g, '')
|
|
2461
|
+
}
|
|
2428
2462
|
|
|
2429
2463
|
// Phase 2: Handle whitespace transformations
|
|
2430
2464
|
if (removeNewlines) {
|
|
@@ -2612,6 +2646,19 @@ function trackingPlugin(schema, options) {
|
|
|
2612
2646
|
next()
|
|
2613
2647
|
})
|
|
2614
2648
|
|
|
2649
|
+
// Add core indexes
|
|
2650
|
+
schema.index({
|
|
2651
|
+
'meta.active': 1,
|
|
2652
|
+
'meta.deleted': 1
|
|
2653
|
+
}, {
|
|
2654
|
+
name: 'tracking_status_index',
|
|
2655
|
+
background: true,
|
|
2656
|
+
partialFilterExpression: {
|
|
2657
|
+
'meta.active': true,
|
|
2658
|
+
'meta.deleted': false
|
|
2659
|
+
}
|
|
2660
|
+
})
|
|
2661
|
+
|
|
2615
2662
|
// Optional: Add helper methods
|
|
2616
2663
|
// schema.methods.touch = function(userId) {
|
|
2617
2664
|
// this.meta.updatedAt = new Date()
|
|
@@ -2619,6 +2666,57 @@ function trackingPlugin(schema, options) {
|
|
|
2619
2666
|
// }
|
|
2620
2667
|
}
|
|
2621
2668
|
|
|
2669
|
+
;// ./lib/helpers/tenantPlugin/tenantPlugin.js
|
|
2670
|
+
|
|
2671
|
+
|
|
2672
|
+
function tenantPlugin(schema, options) {
|
|
2673
|
+
// Apply tracking plugin first if not already present
|
|
2674
|
+
if (!schema.path('meta')) {
|
|
2675
|
+
trackingPlugin(schema, options)
|
|
2676
|
+
}
|
|
2677
|
+
|
|
2678
|
+
// Add tenant-specific fields
|
|
2679
|
+
schema.add({
|
|
2680
|
+
metadata: [{ type: Object }], // Instead of Schema.Types.Mixed
|
|
2681
|
+
remarks: [{ type: Object }],
|
|
2682
|
+
tenantCode: { type: String, required: true }
|
|
2683
|
+
})
|
|
2684
|
+
|
|
2685
|
+
// Add core indexes
|
|
2686
|
+
schema.index({
|
|
2687
|
+
'tenantCode': 1
|
|
2688
|
+
}, {
|
|
2689
|
+
name: 'tenant_core_index',
|
|
2690
|
+
background: true
|
|
2691
|
+
})
|
|
2692
|
+
|
|
2693
|
+
// 1. ENHANCE EXISTING TRACKING INDEXES
|
|
2694
|
+
const existingIndexes = schema.indexes()
|
|
2695
|
+
|
|
2696
|
+
// Check if tracking_status_index exists
|
|
2697
|
+
const hasTenantStatusIndex = existingIndexes.some(idx =>
|
|
2698
|
+
idx.name === 'tenant_status_index' // Check by name for reliability
|
|
2699
|
+
)
|
|
2700
|
+
|
|
2701
|
+
if (!hasTenantStatusIndex) {
|
|
2702
|
+
schema.index({
|
|
2703
|
+
'tenantCode': 1, // Unique field first
|
|
2704
|
+
_type: 1, // Low-cardinality field last
|
|
2705
|
+
}, {
|
|
2706
|
+
name: 'tenant_status_index',
|
|
2707
|
+
background: true,
|
|
2708
|
+
partialFilterExpression: {
|
|
2709
|
+
'_type': 'Tenant',
|
|
2710
|
+
'meta.active': true,
|
|
2711
|
+
'meta.deleted': false
|
|
2712
|
+
}
|
|
2713
|
+
})
|
|
2714
|
+
}
|
|
2715
|
+
}
|
|
2716
|
+
|
|
2717
|
+
;// ./lib/helpers/tenantPlugin/index.js
|
|
2718
|
+
|
|
2719
|
+
|
|
2622
2720
|
;// ./lib/helpers/trackingPlugin/index.js
|
|
2623
2721
|
|
|
2624
2722
|
|
|
@@ -2645,6 +2743,7 @@ function trackingPlugin(schema, options) {
|
|
|
2645
2743
|
|
|
2646
2744
|
|
|
2647
2745
|
|
|
2746
|
+
|
|
2648
2747
|
|
|
2649
2748
|
|
|
2650
2749
|
;// ./lib/models/apiResponse/index.js
|
|
@@ -2986,7 +3085,10 @@ class KeyValueObject {
|
|
|
2986
3085
|
}, [])
|
|
2987
3086
|
}
|
|
2988
3087
|
static sameKey(item, key) {
|
|
2989
|
-
|
|
3088
|
+
if (item) {
|
|
3089
|
+
return _isSame(item.key, key)
|
|
3090
|
+
}
|
|
3091
|
+
return false
|
|
2990
3092
|
}
|
|
2991
3093
|
static toObject(arr = []) {
|
|
2992
3094
|
if (Array.isArray(arr)) {
|
|
@@ -3240,17 +3342,19 @@ class TrackedEntity {
|
|
|
3240
3342
|
constructor(options = {}) {
|
|
3241
3343
|
options = options || {}
|
|
3242
3344
|
const timestamp = Date.now()
|
|
3243
|
-
|
|
3244
|
-
active: options.active ?? true,
|
|
3245
|
-
created: options.created ??
|
|
3246
|
-
|
|
3247
|
-
|
|
3248
|
-
|
|
3249
|
-
|
|
3345
|
+
this.meta = {
|
|
3346
|
+
active: options.meta?.active ?? options.active ?? true,
|
|
3347
|
+
created: options.meta?.created ?? (options.created
|
|
3348
|
+
? new Date(options.created).getTime()
|
|
3349
|
+
: timestamp),
|
|
3350
|
+
creator: options.meta?.creator ?? options.creator ?? '',
|
|
3351
|
+
deleted: options.meta?.deleted ?? options.deleted ?? false,
|
|
3352
|
+
modified: options.meta?.modified ?? (options.modified
|
|
3353
|
+
? new Date(options.modified).getTime()
|
|
3354
|
+
: timestamp),
|
|
3355
|
+
owner: options.meta?.owner ?? options.owner ?? '',
|
|
3250
3356
|
}
|
|
3251
3357
|
|
|
3252
|
-
this.meta = { ..._tracking, ...options.meta }
|
|
3253
|
-
|
|
3254
3358
|
// if (trackFlat) {
|
|
3255
3359
|
// Object.assign(this, _tracking)
|
|
3256
3360
|
// } else {
|
package/dist/q-utilities.esm.js
CHANGED
|
@@ -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
|
-
|
|
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
|
|
@@ -277,6 +290,9 @@ function getValueByKeys_getValueByKeys(keys, data) {
|
|
|
277
290
|
return getValueByKeys_getValueByKeys(_keys, _data[firstKey])
|
|
278
291
|
}
|
|
279
292
|
if (_data && firstKey) {
|
|
293
|
+
if (_keys.length > 0) {
|
|
294
|
+
return getValueByKeys_getValueByKeys(_keys, _data[firstKey])
|
|
295
|
+
}
|
|
280
296
|
return _data[firstKey]
|
|
281
297
|
}
|
|
282
298
|
return _data
|
|
@@ -1330,19 +1346,6 @@ function concatStringByArray(arrTemplate, data) {
|
|
|
1330
1346
|
return arrTemplate.reduce((acc, item) => {
|
|
1331
1347
|
const { type, value = '', restriction, template, format, showMinutes } = item
|
|
1332
1348
|
switch (type) {
|
|
1333
|
-
case('label'): {
|
|
1334
|
-
if (getValidation(restriction, data, getValueByKeys_getValueByKeys)) {
|
|
1335
|
-
acc += (value.toString())
|
|
1336
|
-
}
|
|
1337
|
-
break
|
|
1338
|
-
}
|
|
1339
|
-
case('value'): {
|
|
1340
|
-
if (getValidation(restriction, data, getValueByKeys_getValueByKeys)) {
|
|
1341
|
-
const _value = getValueByKeys_getValueByKeys(value.split('.'), data) || ''
|
|
1342
|
-
acc += (_value.toString())
|
|
1343
|
-
}
|
|
1344
|
-
break
|
|
1345
|
-
}
|
|
1346
1349
|
case('array'): {
|
|
1347
1350
|
if (getValidation(restriction, data, getValueByKeys_getValueByKeys)) {
|
|
1348
1351
|
const _value = getValueByKeys_getValueByKeys(value.split('.'), data) || []
|
|
@@ -1352,6 +1355,13 @@ function concatStringByArray(arrTemplate, data) {
|
|
|
1352
1355
|
}
|
|
1353
1356
|
break
|
|
1354
1357
|
}
|
|
1358
|
+
case ('date'): {
|
|
1359
|
+
if (getValidation(restriction, data, getValueByKeys_getValueByKeys)) {
|
|
1360
|
+
const _value = getValueByKeys_getValueByKeys(value.split('.'), data) || ''
|
|
1361
|
+
acc += (formatDate(_value, format).toString())
|
|
1362
|
+
}
|
|
1363
|
+
break
|
|
1364
|
+
}
|
|
1355
1365
|
case('ellipsis'): {
|
|
1356
1366
|
if (getValidation(restriction, data, getValueByKeys_getValueByKeys)) {
|
|
1357
1367
|
const { maxLength } = item
|
|
@@ -1364,10 +1374,15 @@ function concatStringByArray(arrTemplate, data) {
|
|
|
1364
1374
|
}
|
|
1365
1375
|
break
|
|
1366
1376
|
}
|
|
1367
|
-
case
|
|
1377
|
+
case('group'): {
|
|
1368
1378
|
if (getValidation(restriction, data, getValueByKeys_getValueByKeys)) {
|
|
1369
|
-
|
|
1370
|
-
|
|
1379
|
+
return concatStringByArray(value, data)
|
|
1380
|
+
}
|
|
1381
|
+
break
|
|
1382
|
+
}
|
|
1383
|
+
case('label'): {
|
|
1384
|
+
if (getValidation(restriction, data, getValueByKeys_getValueByKeys)) {
|
|
1385
|
+
acc += (value.toString())
|
|
1371
1386
|
}
|
|
1372
1387
|
break
|
|
1373
1388
|
}
|
|
@@ -1378,6 +1393,13 @@ function concatStringByArray(arrTemplate, data) {
|
|
|
1378
1393
|
}
|
|
1379
1394
|
break
|
|
1380
1395
|
}
|
|
1396
|
+
case('value'): {
|
|
1397
|
+
if (getValidation(restriction, data, getValueByKeys_getValueByKeys)) {
|
|
1398
|
+
const _value = getValueByKeys_getValueByKeys(value.split('.'), data) || ''
|
|
1399
|
+
acc += (_value.toString())
|
|
1400
|
+
}
|
|
1401
|
+
break
|
|
1402
|
+
}
|
|
1381
1403
|
}
|
|
1382
1404
|
return acc
|
|
1383
1405
|
}, '')
|
|
@@ -1398,7 +1420,7 @@ function convertString(string, patternMatch = /\$\{(.+?)\}/g, value, getValueByK
|
|
|
1398
1420
|
if (!string) {
|
|
1399
1421
|
return ''
|
|
1400
1422
|
}
|
|
1401
|
-
let _getValueByKeys = typeof getValueByKeys
|
|
1423
|
+
let _getValueByKeys = typeof getValueByKeys === 'function' ? getValueByKeys : getValueByKeys_getValueByKeys
|
|
1402
1424
|
const reg = new RegExp(patternMatch, 'g')
|
|
1403
1425
|
return string.replace(reg, (match, key) => {
|
|
1404
1426
|
const result = _getValueByKeys({ keys: key.split('.'), obj: value })
|
|
@@ -1419,7 +1441,7 @@ function convertString(string, patternMatch = /\$\{(.+?)\}/g, value, getValueByK
|
|
|
1419
1441
|
|
|
1420
1442
|
;// ./lib/helpers/escapeRegex/escapeRegex.js
|
|
1421
1443
|
function escapeRegex(string) {
|
|
1422
|
-
return string.replace(/[-\/\\^$*+?.()|[\]{}]/g, '\\$&')
|
|
1444
|
+
return String(string).replace(/[-\/\\^$*+?.()|[\]{}]/g, '\\$&')
|
|
1423
1445
|
}
|
|
1424
1446
|
|
|
1425
1447
|
;// ./lib/helpers/escapeRegex/index.js
|
|
@@ -1855,19 +1877,19 @@ class Repo {
|
|
|
1855
1877
|
})
|
|
1856
1878
|
}
|
|
1857
1879
|
|
|
1858
|
-
saveAll({ docs, systemLog }) {
|
|
1880
|
+
saveAll({ config = {}, docs, systemLog }) {
|
|
1859
1881
|
let isNew
|
|
1860
1882
|
const log = _makeLog({
|
|
1861
1883
|
systemLog,
|
|
1862
1884
|
label: 'REPO_WRITE',
|
|
1863
1885
|
message: `fn ${this._classname}.prototype.saveAll`,
|
|
1864
|
-
input: [{ docs: [...docs], systemLog: { ...systemLog } }]
|
|
1886
|
+
input: [{ config, docs: [...docs], systemLog: { ...systemLog } }]
|
|
1865
1887
|
})
|
|
1866
1888
|
const promise = typeof this.model.saveAll === 'function'
|
|
1867
|
-
? this.model.saveAll({ docs })
|
|
1889
|
+
? this.model.saveAll({ config, docs })
|
|
1868
1890
|
: Promise.all(docs.map(async (doc) => {
|
|
1869
1891
|
if (doc) {
|
|
1870
|
-
const result = await this.saveOne({ doc })
|
|
1892
|
+
const result = await this.saveOne({ config, doc })
|
|
1871
1893
|
isNew = result.isNew
|
|
1872
1894
|
const _data = result._data || result.data
|
|
1873
1895
|
return _data[0]
|
|
@@ -1889,15 +1911,19 @@ class Repo {
|
|
|
1889
1911
|
})
|
|
1890
1912
|
}
|
|
1891
1913
|
|
|
1892
|
-
saveOne({ doc, systemLog }) {
|
|
1914
|
+
saveOne({ config = {}, doc, systemLog }) {
|
|
1893
1915
|
const log = _makeLog({
|
|
1894
1916
|
systemLog,
|
|
1895
1917
|
label: 'REPO_WRITE',
|
|
1896
1918
|
message: `fn ${this._classname}.prototype.saveOne`,
|
|
1897
|
-
input: [{ doc: { ...doc }, systemLog: { ...systemLog } }]
|
|
1919
|
+
input: [{ config, doc: { ...doc }, systemLog: { ...systemLog } }]
|
|
1898
1920
|
})
|
|
1921
|
+
const saveOptions = {
|
|
1922
|
+
...this.saveOptions,
|
|
1923
|
+
...config,
|
|
1924
|
+
}
|
|
1899
1925
|
return new Promise((resolve, reject) => {
|
|
1900
|
-
this.model.saveOne(doc,
|
|
1926
|
+
this.model.saveOne(doc, saveOptions, (err, result) => {
|
|
1901
1927
|
if (err) {
|
|
1902
1928
|
log({ level: 'warn', output: err.toString() })
|
|
1903
1929
|
reject(err)
|
|
@@ -2039,11 +2065,11 @@ class Service {
|
|
|
2039
2065
|
return this.initFromArray(arr).filter((i) => i)
|
|
2040
2066
|
}
|
|
2041
2067
|
|
|
2042
|
-
async saveAll({
|
|
2068
|
+
async saveAll({ config = {}, docs = [], systemLog } = {}) {
|
|
2043
2069
|
const copies = docs.map((doc) => {
|
|
2044
2070
|
return config.skipInit ? doc : this.init(doc)
|
|
2045
2071
|
})
|
|
2046
|
-
const result = await this.repo.saveAll({ docs: copies, systemLog })
|
|
2072
|
+
const result = await this.repo.saveAll({ config, docs: copies, systemLog })
|
|
2047
2073
|
return makeApiResponse({
|
|
2048
2074
|
repo: this.repo,
|
|
2049
2075
|
result
|
|
@@ -2051,10 +2077,10 @@ class Service {
|
|
|
2051
2077
|
}
|
|
2052
2078
|
|
|
2053
2079
|
// set skipInit to true if we want to use POST for query
|
|
2054
|
-
async saveOne({
|
|
2080
|
+
async saveOne({ config = {}, doc = {}, systemLog } = {}) {
|
|
2055
2081
|
const copy = config.skipInit ? doc : this.init(doc)
|
|
2056
2082
|
if (copy) {
|
|
2057
|
-
const result = await this.repo.saveOne({ doc: copy, systemLog })
|
|
2083
|
+
const result = await this.repo.saveOne({ config, doc: copy, systemLog })
|
|
2058
2084
|
return makeApiResponse({
|
|
2059
2085
|
repo: this.repo,
|
|
2060
2086
|
result
|
|
@@ -2327,6 +2353,7 @@ function sanitizeText(input, options = {}) {
|
|
|
2327
2353
|
normalizeWhitespace = true,
|
|
2328
2354
|
removeNewlines = false,
|
|
2329
2355
|
trim = true,
|
|
2356
|
+
preserveBasicWhitespace = true, // new option to keep tabs, newlines if removeNewlines=false
|
|
2330
2357
|
} = options
|
|
2331
2358
|
|
|
2332
2359
|
if (typeof input !== 'string') {
|
|
@@ -2335,8 +2362,14 @@ function sanitizeText(input, options = {}) {
|
|
|
2335
2362
|
|
|
2336
2363
|
let result = input
|
|
2337
2364
|
|
|
2338
|
-
// Phase 1: Remove
|
|
2339
|
-
|
|
2365
|
+
// Phase 1: Remove all control characters except basic whitespace if requested
|
|
2366
|
+
if (preserveBasicWhitespace && !removeNewlines) {
|
|
2367
|
+
// Keep tab (\t), newline (\n), carriage return (\r)
|
|
2368
|
+
result = result.replace(/[\x00-\x08\x0B\x0C\x0E-\x1F\x7F-\x9F\u200B-\u200D\uFEFF\u202A-\u202E]/g, '')
|
|
2369
|
+
} else {
|
|
2370
|
+
// Remove all control characters including basic whitespace
|
|
2371
|
+
result = result.replace(/[\x00-\x1F\x7F-\x9F\u200B-\u200D\uFEFF\u202A-\u202E]/g, '')
|
|
2372
|
+
}
|
|
2340
2373
|
|
|
2341
2374
|
// Phase 2: Handle whitespace transformations
|
|
2342
2375
|
if (removeNewlines) {
|
|
@@ -2524,6 +2557,19 @@ function trackingPlugin(schema, options) {
|
|
|
2524
2557
|
next()
|
|
2525
2558
|
})
|
|
2526
2559
|
|
|
2560
|
+
// Add core indexes
|
|
2561
|
+
schema.index({
|
|
2562
|
+
'meta.active': 1,
|
|
2563
|
+
'meta.deleted': 1
|
|
2564
|
+
}, {
|
|
2565
|
+
name: 'tracking_status_index',
|
|
2566
|
+
background: true,
|
|
2567
|
+
partialFilterExpression: {
|
|
2568
|
+
'meta.active': true,
|
|
2569
|
+
'meta.deleted': false
|
|
2570
|
+
}
|
|
2571
|
+
})
|
|
2572
|
+
|
|
2527
2573
|
// Optional: Add helper methods
|
|
2528
2574
|
// schema.methods.touch = function(userId) {
|
|
2529
2575
|
// this.meta.updatedAt = new Date()
|
|
@@ -2531,6 +2577,57 @@ function trackingPlugin(schema, options) {
|
|
|
2531
2577
|
// }
|
|
2532
2578
|
}
|
|
2533
2579
|
|
|
2580
|
+
;// ./lib/helpers/tenantPlugin/tenantPlugin.js
|
|
2581
|
+
|
|
2582
|
+
|
|
2583
|
+
function tenantPlugin(schema, options) {
|
|
2584
|
+
// Apply tracking plugin first if not already present
|
|
2585
|
+
if (!schema.path('meta')) {
|
|
2586
|
+
trackingPlugin(schema, options)
|
|
2587
|
+
}
|
|
2588
|
+
|
|
2589
|
+
// Add tenant-specific fields
|
|
2590
|
+
schema.add({
|
|
2591
|
+
metadata: [{ type: Object }], // Instead of Schema.Types.Mixed
|
|
2592
|
+
remarks: [{ type: Object }],
|
|
2593
|
+
tenantCode: { type: String, required: true }
|
|
2594
|
+
})
|
|
2595
|
+
|
|
2596
|
+
// Add core indexes
|
|
2597
|
+
schema.index({
|
|
2598
|
+
'tenantCode': 1
|
|
2599
|
+
}, {
|
|
2600
|
+
name: 'tenant_core_index',
|
|
2601
|
+
background: true
|
|
2602
|
+
})
|
|
2603
|
+
|
|
2604
|
+
// 1. ENHANCE EXISTING TRACKING INDEXES
|
|
2605
|
+
const existingIndexes = schema.indexes()
|
|
2606
|
+
|
|
2607
|
+
// Check if tracking_status_index exists
|
|
2608
|
+
const hasTenantStatusIndex = existingIndexes.some(idx =>
|
|
2609
|
+
idx.name === 'tenant_status_index' // Check by name for reliability
|
|
2610
|
+
)
|
|
2611
|
+
|
|
2612
|
+
if (!hasTenantStatusIndex) {
|
|
2613
|
+
schema.index({
|
|
2614
|
+
'tenantCode': 1, // Unique field first
|
|
2615
|
+
_type: 1, // Low-cardinality field last
|
|
2616
|
+
}, {
|
|
2617
|
+
name: 'tenant_status_index',
|
|
2618
|
+
background: true,
|
|
2619
|
+
partialFilterExpression: {
|
|
2620
|
+
'_type': 'Tenant',
|
|
2621
|
+
'meta.active': true,
|
|
2622
|
+
'meta.deleted': false
|
|
2623
|
+
}
|
|
2624
|
+
})
|
|
2625
|
+
}
|
|
2626
|
+
}
|
|
2627
|
+
|
|
2628
|
+
;// ./lib/helpers/tenantPlugin/index.js
|
|
2629
|
+
|
|
2630
|
+
|
|
2534
2631
|
;// ./lib/helpers/trackingPlugin/index.js
|
|
2535
2632
|
|
|
2536
2633
|
|
|
@@ -2557,6 +2654,7 @@ function trackingPlugin(schema, options) {
|
|
|
2557
2654
|
|
|
2558
2655
|
|
|
2559
2656
|
|
|
2657
|
+
|
|
2560
2658
|
|
|
2561
2659
|
|
|
2562
2660
|
;// ./lib/models/apiResponse/index.js
|
|
@@ -2898,7 +2996,10 @@ class KeyValueObject {
|
|
|
2898
2996
|
}, [])
|
|
2899
2997
|
}
|
|
2900
2998
|
static sameKey(item, key) {
|
|
2901
|
-
|
|
2999
|
+
if (item) {
|
|
3000
|
+
return _isSame(item.key, key)
|
|
3001
|
+
}
|
|
3002
|
+
return false
|
|
2902
3003
|
}
|
|
2903
3004
|
static toObject(arr = []) {
|
|
2904
3005
|
if (Array.isArray(arr)) {
|
|
@@ -3152,17 +3253,19 @@ class TrackedEntity {
|
|
|
3152
3253
|
constructor(options = {}) {
|
|
3153
3254
|
options = options || {}
|
|
3154
3255
|
const timestamp = Date.now()
|
|
3155
|
-
|
|
3156
|
-
active: options.active ?? true,
|
|
3157
|
-
created: options.created ??
|
|
3158
|
-
|
|
3159
|
-
|
|
3160
|
-
|
|
3161
|
-
|
|
3256
|
+
this.meta = {
|
|
3257
|
+
active: options.meta?.active ?? options.active ?? true,
|
|
3258
|
+
created: options.meta?.created ?? (options.created
|
|
3259
|
+
? new Date(options.created).getTime()
|
|
3260
|
+
: timestamp),
|
|
3261
|
+
creator: options.meta?.creator ?? options.creator ?? '',
|
|
3262
|
+
deleted: options.meta?.deleted ?? options.deleted ?? false,
|
|
3263
|
+
modified: options.meta?.modified ?? (options.modified
|
|
3264
|
+
? new Date(options.modified).getTime()
|
|
3265
|
+
: timestamp),
|
|
3266
|
+
owner: options.meta?.owner ?? options.owner ?? '',
|
|
3162
3267
|
}
|
|
3163
3268
|
|
|
3164
|
-
this.meta = { ..._tracking, ...options.meta }
|
|
3165
|
-
|
|
3166
3269
|
// if (trackFlat) {
|
|
3167
3270
|
// Object.assign(this, _tracking)
|
|
3168
3271
|
// } else {
|
|
@@ -3431,4 +3534,4 @@ function _makeSetCode(fieldName, options) {
|
|
|
3431
3534
|
;// ./index.js
|
|
3432
3535
|
|
|
3433
3536
|
|
|
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 };
|
|
3537
|
+
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 };
|
package/dist/q-utilities.min.js
CHANGED
|
@@ -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
|
-
|
|
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
|
|
@@ -364,6 +378,9 @@ function getValueByKeys_getValueByKeys(keys, data) {
|
|
|
364
378
|
return getValueByKeys_getValueByKeys(_keys, _data[firstKey])
|
|
365
379
|
}
|
|
366
380
|
if (_data && firstKey) {
|
|
381
|
+
if (_keys.length > 0) {
|
|
382
|
+
return getValueByKeys_getValueByKeys(_keys, _data[firstKey])
|
|
383
|
+
}
|
|
367
384
|
return _data[firstKey]
|
|
368
385
|
}
|
|
369
386
|
return _data
|
|
@@ -1417,19 +1434,6 @@ function concatStringByArray(arrTemplate, data) {
|
|
|
1417
1434
|
return arrTemplate.reduce((acc, item) => {
|
|
1418
1435
|
const { type, value = '', restriction, template, format, showMinutes } = item
|
|
1419
1436
|
switch (type) {
|
|
1420
|
-
case('label'): {
|
|
1421
|
-
if (getValidation(restriction, data, getValueByKeys_getValueByKeys)) {
|
|
1422
|
-
acc += (value.toString())
|
|
1423
|
-
}
|
|
1424
|
-
break
|
|
1425
|
-
}
|
|
1426
|
-
case('value'): {
|
|
1427
|
-
if (getValidation(restriction, data, getValueByKeys_getValueByKeys)) {
|
|
1428
|
-
const _value = getValueByKeys_getValueByKeys(value.split('.'), data) || ''
|
|
1429
|
-
acc += (_value.toString())
|
|
1430
|
-
}
|
|
1431
|
-
break
|
|
1432
|
-
}
|
|
1433
1437
|
case('array'): {
|
|
1434
1438
|
if (getValidation(restriction, data, getValueByKeys_getValueByKeys)) {
|
|
1435
1439
|
const _value = getValueByKeys_getValueByKeys(value.split('.'), data) || []
|
|
@@ -1439,6 +1443,13 @@ function concatStringByArray(arrTemplate, data) {
|
|
|
1439
1443
|
}
|
|
1440
1444
|
break
|
|
1441
1445
|
}
|
|
1446
|
+
case ('date'): {
|
|
1447
|
+
if (getValidation(restriction, data, getValueByKeys_getValueByKeys)) {
|
|
1448
|
+
const _value = getValueByKeys_getValueByKeys(value.split('.'), data) || ''
|
|
1449
|
+
acc += (formatDate(_value, format).toString())
|
|
1450
|
+
}
|
|
1451
|
+
break
|
|
1452
|
+
}
|
|
1442
1453
|
case('ellipsis'): {
|
|
1443
1454
|
if (getValidation(restriction, data, getValueByKeys_getValueByKeys)) {
|
|
1444
1455
|
const { maxLength } = item
|
|
@@ -1451,10 +1462,15 @@ function concatStringByArray(arrTemplate, data) {
|
|
|
1451
1462
|
}
|
|
1452
1463
|
break
|
|
1453
1464
|
}
|
|
1454
|
-
case
|
|
1465
|
+
case('group'): {
|
|
1455
1466
|
if (getValidation(restriction, data, getValueByKeys_getValueByKeys)) {
|
|
1456
|
-
|
|
1457
|
-
|
|
1467
|
+
return concatStringByArray(value, data)
|
|
1468
|
+
}
|
|
1469
|
+
break
|
|
1470
|
+
}
|
|
1471
|
+
case('label'): {
|
|
1472
|
+
if (getValidation(restriction, data, getValueByKeys_getValueByKeys)) {
|
|
1473
|
+
acc += (value.toString())
|
|
1458
1474
|
}
|
|
1459
1475
|
break
|
|
1460
1476
|
}
|
|
@@ -1465,6 +1481,13 @@ function concatStringByArray(arrTemplate, data) {
|
|
|
1465
1481
|
}
|
|
1466
1482
|
break
|
|
1467
1483
|
}
|
|
1484
|
+
case('value'): {
|
|
1485
|
+
if (getValidation(restriction, data, getValueByKeys_getValueByKeys)) {
|
|
1486
|
+
const _value = getValueByKeys_getValueByKeys(value.split('.'), data) || ''
|
|
1487
|
+
acc += (_value.toString())
|
|
1488
|
+
}
|
|
1489
|
+
break
|
|
1490
|
+
}
|
|
1468
1491
|
}
|
|
1469
1492
|
return acc
|
|
1470
1493
|
}, '')
|
|
@@ -1485,7 +1508,7 @@ function convertString(string, patternMatch = /\$\{(.+?)\}/g, value, getValueByK
|
|
|
1485
1508
|
if (!string) {
|
|
1486
1509
|
return ''
|
|
1487
1510
|
}
|
|
1488
|
-
let _getValueByKeys = typeof getValueByKeys
|
|
1511
|
+
let _getValueByKeys = typeof getValueByKeys === 'function' ? getValueByKeys : getValueByKeys_getValueByKeys
|
|
1489
1512
|
const reg = new RegExp(patternMatch, 'g')
|
|
1490
1513
|
return string.replace(reg, (match, key) => {
|
|
1491
1514
|
const result = _getValueByKeys({ keys: key.split('.'), obj: value })
|
|
@@ -1506,7 +1529,7 @@ function convertString(string, patternMatch = /\$\{(.+?)\}/g, value, getValueByK
|
|
|
1506
1529
|
|
|
1507
1530
|
;// ./lib/helpers/escapeRegex/escapeRegex.js
|
|
1508
1531
|
function escapeRegex(string) {
|
|
1509
|
-
return string.replace(/[-\/\\^$*+?.()|[\]{}]/g, '\\$&')
|
|
1532
|
+
return String(string).replace(/[-\/\\^$*+?.()|[\]{}]/g, '\\$&')
|
|
1510
1533
|
}
|
|
1511
1534
|
|
|
1512
1535
|
;// ./lib/helpers/escapeRegex/index.js
|
|
@@ -1942,19 +1965,19 @@ class Repo {
|
|
|
1942
1965
|
})
|
|
1943
1966
|
}
|
|
1944
1967
|
|
|
1945
|
-
saveAll({ docs, systemLog }) {
|
|
1968
|
+
saveAll({ config = {}, docs, systemLog }) {
|
|
1946
1969
|
let isNew
|
|
1947
1970
|
const log = _makeLog({
|
|
1948
1971
|
systemLog,
|
|
1949
1972
|
label: 'REPO_WRITE',
|
|
1950
1973
|
message: `fn ${this._classname}.prototype.saveAll`,
|
|
1951
|
-
input: [{ docs: [...docs], systemLog: { ...systemLog } }]
|
|
1974
|
+
input: [{ config, docs: [...docs], systemLog: { ...systemLog } }]
|
|
1952
1975
|
})
|
|
1953
1976
|
const promise = typeof this.model.saveAll === 'function'
|
|
1954
|
-
? this.model.saveAll({ docs })
|
|
1977
|
+
? this.model.saveAll({ config, docs })
|
|
1955
1978
|
: Promise.all(docs.map(async (doc) => {
|
|
1956
1979
|
if (doc) {
|
|
1957
|
-
const result = await this.saveOne({ doc })
|
|
1980
|
+
const result = await this.saveOne({ config, doc })
|
|
1958
1981
|
isNew = result.isNew
|
|
1959
1982
|
const _data = result._data || result.data
|
|
1960
1983
|
return _data[0]
|
|
@@ -1976,15 +1999,19 @@ class Repo {
|
|
|
1976
1999
|
})
|
|
1977
2000
|
}
|
|
1978
2001
|
|
|
1979
|
-
saveOne({ doc, systemLog }) {
|
|
2002
|
+
saveOne({ config = {}, doc, systemLog }) {
|
|
1980
2003
|
const log = _makeLog({
|
|
1981
2004
|
systemLog,
|
|
1982
2005
|
label: 'REPO_WRITE',
|
|
1983
2006
|
message: `fn ${this._classname}.prototype.saveOne`,
|
|
1984
|
-
input: [{ doc: { ...doc }, systemLog: { ...systemLog } }]
|
|
2007
|
+
input: [{ config, doc: { ...doc }, systemLog: { ...systemLog } }]
|
|
1985
2008
|
})
|
|
2009
|
+
const saveOptions = {
|
|
2010
|
+
...this.saveOptions,
|
|
2011
|
+
...config,
|
|
2012
|
+
}
|
|
1986
2013
|
return new Promise((resolve, reject) => {
|
|
1987
|
-
this.model.saveOne(doc,
|
|
2014
|
+
this.model.saveOne(doc, saveOptions, (err, result) => {
|
|
1988
2015
|
if (err) {
|
|
1989
2016
|
log({ level: 'warn', output: err.toString() })
|
|
1990
2017
|
reject(err)
|
|
@@ -2126,11 +2153,11 @@ class Service {
|
|
|
2126
2153
|
return this.initFromArray(arr).filter((i) => i)
|
|
2127
2154
|
}
|
|
2128
2155
|
|
|
2129
|
-
async saveAll({
|
|
2156
|
+
async saveAll({ config = {}, docs = [], systemLog } = {}) {
|
|
2130
2157
|
const copies = docs.map((doc) => {
|
|
2131
2158
|
return config.skipInit ? doc : this.init(doc)
|
|
2132
2159
|
})
|
|
2133
|
-
const result = await this.repo.saveAll({ docs: copies, systemLog })
|
|
2160
|
+
const result = await this.repo.saveAll({ config, docs: copies, systemLog })
|
|
2134
2161
|
return makeApiResponse({
|
|
2135
2162
|
repo: this.repo,
|
|
2136
2163
|
result
|
|
@@ -2138,10 +2165,10 @@ class Service {
|
|
|
2138
2165
|
}
|
|
2139
2166
|
|
|
2140
2167
|
// set skipInit to true if we want to use POST for query
|
|
2141
|
-
async saveOne({
|
|
2168
|
+
async saveOne({ config = {}, doc = {}, systemLog } = {}) {
|
|
2142
2169
|
const copy = config.skipInit ? doc : this.init(doc)
|
|
2143
2170
|
if (copy) {
|
|
2144
|
-
const result = await this.repo.saveOne({ doc: copy, systemLog })
|
|
2171
|
+
const result = await this.repo.saveOne({ config, doc: copy, systemLog })
|
|
2145
2172
|
return makeApiResponse({
|
|
2146
2173
|
repo: this.repo,
|
|
2147
2174
|
result
|
|
@@ -2414,6 +2441,7 @@ function sanitizeText(input, options = {}) {
|
|
|
2414
2441
|
normalizeWhitespace = true,
|
|
2415
2442
|
removeNewlines = false,
|
|
2416
2443
|
trim = true,
|
|
2444
|
+
preserveBasicWhitespace = true, // new option to keep tabs, newlines if removeNewlines=false
|
|
2417
2445
|
} = options
|
|
2418
2446
|
|
|
2419
2447
|
if (typeof input !== 'string') {
|
|
@@ -2422,8 +2450,14 @@ function sanitizeText(input, options = {}) {
|
|
|
2422
2450
|
|
|
2423
2451
|
let result = input
|
|
2424
2452
|
|
|
2425
|
-
// Phase 1: Remove
|
|
2426
|
-
|
|
2453
|
+
// Phase 1: Remove all control characters except basic whitespace if requested
|
|
2454
|
+
if (preserveBasicWhitespace && !removeNewlines) {
|
|
2455
|
+
// Keep tab (\t), newline (\n), carriage return (\r)
|
|
2456
|
+
result = result.replace(/[\x00-\x08\x0B\x0C\x0E-\x1F\x7F-\x9F\u200B-\u200D\uFEFF\u202A-\u202E]/g, '')
|
|
2457
|
+
} else {
|
|
2458
|
+
// Remove all control characters including basic whitespace
|
|
2459
|
+
result = result.replace(/[\x00-\x1F\x7F-\x9F\u200B-\u200D\uFEFF\u202A-\u202E]/g, '')
|
|
2460
|
+
}
|
|
2427
2461
|
|
|
2428
2462
|
// Phase 2: Handle whitespace transformations
|
|
2429
2463
|
if (removeNewlines) {
|
|
@@ -2611,6 +2645,19 @@ function trackingPlugin(schema, options) {
|
|
|
2611
2645
|
next()
|
|
2612
2646
|
})
|
|
2613
2647
|
|
|
2648
|
+
// Add core indexes
|
|
2649
|
+
schema.index({
|
|
2650
|
+
'meta.active': 1,
|
|
2651
|
+
'meta.deleted': 1
|
|
2652
|
+
}, {
|
|
2653
|
+
name: 'tracking_status_index',
|
|
2654
|
+
background: true,
|
|
2655
|
+
partialFilterExpression: {
|
|
2656
|
+
'meta.active': true,
|
|
2657
|
+
'meta.deleted': false
|
|
2658
|
+
}
|
|
2659
|
+
})
|
|
2660
|
+
|
|
2614
2661
|
// Optional: Add helper methods
|
|
2615
2662
|
// schema.methods.touch = function(userId) {
|
|
2616
2663
|
// this.meta.updatedAt = new Date()
|
|
@@ -2618,6 +2665,57 @@ function trackingPlugin(schema, options) {
|
|
|
2618
2665
|
// }
|
|
2619
2666
|
}
|
|
2620
2667
|
|
|
2668
|
+
;// ./lib/helpers/tenantPlugin/tenantPlugin.js
|
|
2669
|
+
|
|
2670
|
+
|
|
2671
|
+
function tenantPlugin(schema, options) {
|
|
2672
|
+
// Apply tracking plugin first if not already present
|
|
2673
|
+
if (!schema.path('meta')) {
|
|
2674
|
+
trackingPlugin(schema, options)
|
|
2675
|
+
}
|
|
2676
|
+
|
|
2677
|
+
// Add tenant-specific fields
|
|
2678
|
+
schema.add({
|
|
2679
|
+
metadata: [{ type: Object }], // Instead of Schema.Types.Mixed
|
|
2680
|
+
remarks: [{ type: Object }],
|
|
2681
|
+
tenantCode: { type: String, required: true }
|
|
2682
|
+
})
|
|
2683
|
+
|
|
2684
|
+
// Add core indexes
|
|
2685
|
+
schema.index({
|
|
2686
|
+
'tenantCode': 1
|
|
2687
|
+
}, {
|
|
2688
|
+
name: 'tenant_core_index',
|
|
2689
|
+
background: true
|
|
2690
|
+
})
|
|
2691
|
+
|
|
2692
|
+
// 1. ENHANCE EXISTING TRACKING INDEXES
|
|
2693
|
+
const existingIndexes = schema.indexes()
|
|
2694
|
+
|
|
2695
|
+
// Check if tracking_status_index exists
|
|
2696
|
+
const hasTenantStatusIndex = existingIndexes.some(idx =>
|
|
2697
|
+
idx.name === 'tenant_status_index' // Check by name for reliability
|
|
2698
|
+
)
|
|
2699
|
+
|
|
2700
|
+
if (!hasTenantStatusIndex) {
|
|
2701
|
+
schema.index({
|
|
2702
|
+
'tenantCode': 1, // Unique field first
|
|
2703
|
+
_type: 1, // Low-cardinality field last
|
|
2704
|
+
}, {
|
|
2705
|
+
name: 'tenant_status_index',
|
|
2706
|
+
background: true,
|
|
2707
|
+
partialFilterExpression: {
|
|
2708
|
+
'_type': 'Tenant',
|
|
2709
|
+
'meta.active': true,
|
|
2710
|
+
'meta.deleted': false
|
|
2711
|
+
}
|
|
2712
|
+
})
|
|
2713
|
+
}
|
|
2714
|
+
}
|
|
2715
|
+
|
|
2716
|
+
;// ./lib/helpers/tenantPlugin/index.js
|
|
2717
|
+
|
|
2718
|
+
|
|
2621
2719
|
;// ./lib/helpers/trackingPlugin/index.js
|
|
2622
2720
|
|
|
2623
2721
|
|
|
@@ -2644,6 +2742,7 @@ function trackingPlugin(schema, options) {
|
|
|
2644
2742
|
|
|
2645
2743
|
|
|
2646
2744
|
|
|
2745
|
+
|
|
2647
2746
|
|
|
2648
2747
|
|
|
2649
2748
|
;// ./lib/models/apiResponse/index.js
|
|
@@ -2985,7 +3084,10 @@ class KeyValueObject {
|
|
|
2985
3084
|
}, [])
|
|
2986
3085
|
}
|
|
2987
3086
|
static sameKey(item, key) {
|
|
2988
|
-
|
|
3087
|
+
if (item) {
|
|
3088
|
+
return _isSame(item.key, key)
|
|
3089
|
+
}
|
|
3090
|
+
return false
|
|
2989
3091
|
}
|
|
2990
3092
|
static toObject(arr = []) {
|
|
2991
3093
|
if (Array.isArray(arr)) {
|
|
@@ -3239,17 +3341,19 @@ class TrackedEntity {
|
|
|
3239
3341
|
constructor(options = {}) {
|
|
3240
3342
|
options = options || {}
|
|
3241
3343
|
const timestamp = Date.now()
|
|
3242
|
-
|
|
3243
|
-
active: options.active ?? true,
|
|
3244
|
-
created: options.created ??
|
|
3245
|
-
|
|
3246
|
-
|
|
3247
|
-
|
|
3248
|
-
|
|
3344
|
+
this.meta = {
|
|
3345
|
+
active: options.meta?.active ?? options.active ?? true,
|
|
3346
|
+
created: options.meta?.created ?? (options.created
|
|
3347
|
+
? new Date(options.created).getTime()
|
|
3348
|
+
: timestamp),
|
|
3349
|
+
creator: options.meta?.creator ?? options.creator ?? '',
|
|
3350
|
+
deleted: options.meta?.deleted ?? options.deleted ?? false,
|
|
3351
|
+
modified: options.meta?.modified ?? (options.modified
|
|
3352
|
+
? new Date(options.modified).getTime()
|
|
3353
|
+
: timestamp),
|
|
3354
|
+
owner: options.meta?.owner ?? options.owner ?? '',
|
|
3249
3355
|
}
|
|
3250
3356
|
|
|
3251
|
-
this.meta = { ..._tracking, ...options.meta }
|
|
3252
|
-
|
|
3253
3357
|
// if (trackFlat) {
|
|
3254
3358
|
// Object.assign(this, _tracking)
|
|
3255
3359
|
// } else {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@questwork/q-utilities",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.17",
|
|
4
4
|
"description": "Questwork QUtilities",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"exports": {
|
|
@@ -10,12 +10,6 @@
|
|
|
10
10
|
"default": "./dist/q-utilities.min.js"
|
|
11
11
|
}
|
|
12
12
|
},
|
|
13
|
-
"scripts": {
|
|
14
|
-
"build": "cross-env NODE_ENV=production minimize=false gulp",
|
|
15
|
-
"build:wp": "cross-env NODE_ENV=production minimize=false gulp wp",
|
|
16
|
-
"lint": "eslint .",
|
|
17
|
-
"test:models": "NODE_ENV=test mocha --exit 'lib/models/test.setup.js' 'lib/models/**/*.spec.js'"
|
|
18
|
-
},
|
|
19
13
|
"author": {
|
|
20
14
|
"name": "Questwork Consulting Limited",
|
|
21
15
|
"email": "info@questwork.com",
|
|
@@ -45,5 +39,11 @@
|
|
|
45
39
|
},
|
|
46
40
|
"engines": {
|
|
47
41
|
"node": ">=10.0.0"
|
|
42
|
+
},
|
|
43
|
+
"scripts": {
|
|
44
|
+
"build": "cross-env NODE_ENV=production minimize=false gulp",
|
|
45
|
+
"build:wp": "cross-env NODE_ENV=production minimize=false gulp wp",
|
|
46
|
+
"lint": "eslint .",
|
|
47
|
+
"test:models": "NODE_ENV=test mocha --exit 'lib/models/test.setup.js' 'lib/models/**/*.spec.js'"
|
|
48
48
|
}
|
|
49
|
-
}
|
|
49
|
+
}
|