@questwork/q-utilities 0.1.12 → 0.1.14

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.
@@ -55,22 +55,64 @@ __webpack_require__.d(__webpack_exports__, {
55
55
  Repo: () => (/* reexport */ Repo),
56
56
  Service: () => (/* reexport */ Service),
57
57
  TemplateCompiler: () => (/* reexport */ TemplateCompiler),
58
+ TenantAwareEntity: () => (/* reexport */ TenantAwareEntity),
59
+ TrackedEntity: () => (/* reexport */ TrackedEntity),
58
60
  UniqueKeyGenerator: () => (/* reexport */ UniqueKeyGenerator),
61
+ authorize: () => (/* reexport */ authorize),
59
62
  concatStringByArray: () => (/* reexport */ concatStringByArray),
60
63
  convertString: () => (/* reexport */ convertString),
61
64
  formatDate: () => (/* reexport */ formatDate),
62
65
  generalPost: () => (/* reexport */ generalPost),
63
66
  getValidation: () => (/* reexport */ getValidation),
64
67
  getValueByKeys: () => (/* reexport */ getValueByKeys_getValueByKeys),
68
+ init: () => (/* reexport */ init),
69
+ initFromArray: () => (/* reexport */ initFromArray),
70
+ initOnlyValidFromArray: () => (/* reexport */ initOnlyValidFromArray),
65
71
  makeApiResponse: () => (/* reexport */ makeApiResponse),
66
72
  makeService: () => (/* reexport */ makeService),
67
73
  objectHelper: () => (/* reexport */ objectHelper),
68
74
  pReduce: () => (/* reexport */ pReduce),
69
75
  padZeros: () => (/* reexport */ padZeros),
70
76
  stringFormatter: () => (/* reexport */ stringFormatter),
71
- stringHelper: () => (/* reexport */ stringHelper)
77
+ stringHelper: () => (/* reexport */ stringHelper),
78
+ trackingPlugin: () => (/* reexport */ trackingPlugin)
72
79
  });
73
80
 
81
+ ;// ./lib/helpers/authorize/authorize.js
82
+ function authorize({ allowOwner, query = {}, required, user }) {
83
+ if (!user) {
84
+ throw new Error('Require login.')
85
+ }
86
+ if (!user.permission) {
87
+ throw new Error('You do not have any permission.')
88
+ }
89
+ const scopes = user.permission.getScopes(required || {})
90
+ if (!scopes || scopes.length === 0) {
91
+ throw new Error('You are not allowed in this scope.')
92
+ }
93
+ if (!scopes.includes('*')) {
94
+ query.tenantCode = user.tenantCode
95
+ }
96
+ if (!scopes.includes('TENANT')) {
97
+ query.eventShortCode = user.eventShortCode
98
+ }
99
+ if (!scopes.includes('EVENT')) {
100
+ query.eventRegistrationCode = user.eventRegistrationCode
101
+ }
102
+ if (allowOwner) {
103
+ query.__ALLOW_OWNER = true
104
+ }
105
+ // not good, just use it as example
106
+ if (user.hasExcludedFields) {
107
+ query.__EXCLUDED_FIELDS = user.getExcludedFields(required)
108
+ }
109
+ query.__LOGIN_SUBJECT_CODE = user.loginSubjectCode
110
+ return query
111
+ }
112
+
113
+ ;// ./lib/helpers/authorize/index.js
114
+
115
+
74
116
  ;// ./lib/helpers/getValidation/getValidation.js
75
117
  function getValidation(rule, data, getDataByKey, KeyValueObject) {
76
118
  if (!rule) {
@@ -288,7 +330,6 @@ function getValueByKeys_getValueByKeys(keys, data) {
288
330
  });
289
331
 
290
332
 
291
-
292
333
  ;// ./lib/helpers/getValueByKeys/index.js
293
334
 
294
335
 
@@ -316,7 +357,26 @@ class TemplateCompilerException extends Error {
316
357
  ;// ./lib/models/templateCompiler/constants.js
317
358
  const _EMPTY = '_EMPTY'
318
359
  const _FN_NAMES = [
319
- 'get', 'map', 'join', 'concatIf', 'exec', 'filterOne', 'filterAll', 'formatDate', 'eq', 'neq', 'gt', 'lt', 'gte', 'lte', 'isEmpty', 'isNotEmpty', 'toLowerCase', 'toUpperCase'
360
+ 'concatIf',
361
+ 'divide',
362
+ 'eq',
363
+ 'exec',
364
+ 'filterAll',
365
+ 'filterOne',
366
+ 'formatDate',
367
+ 'get',
368
+ 'gt',
369
+ 'gte',
370
+ 'isEmpty',
371
+ 'isNotEmpty',
372
+ 'join',
373
+ 'lt',
374
+ 'lte',
375
+ 'map',
376
+ 'neq',
377
+ 'removeHtml',
378
+ 'toLowerCase',
379
+ 'toUpperCase',
320
380
  ]
321
381
  const _HIDE = '_HIDE'
322
382
  const _NOT_EMPTY = '_NOT_EMPTY'
@@ -357,6 +417,20 @@ function _concatIf(data, args) {
357
417
 
358
418
 
359
419
 
420
+ ;// ./lib/models/templateCompiler/helpers/_divide.js
421
+ function _divide(value, divisor) {
422
+ try {
423
+ if (Number.isNaN(value)) {
424
+ return value
425
+ }
426
+ return (value / divisor)
427
+ } catch (e) {
428
+ throw e
429
+ }
430
+ }
431
+
432
+
433
+
360
434
  ;// ./lib/models/templateCompiler/helpers/_eq.js
361
435
 
362
436
 
@@ -889,6 +963,66 @@ function _neq(data, args) {
889
963
 
890
964
 
891
965
 
966
+ ;// ./lib/models/templateCompiler/helpers/_removeHtml.js
967
+
968
+
969
+ function _removeHtml(html, args) {
970
+ if (html === null || html === undefined) {
971
+ return null
972
+ }
973
+ if (!Array.isArray(args)) {
974
+ throw new TemplateCompilerException(`_removeHtml: ${TEMPLATE_COMPILER_EXCEPTION_TYPE.argumentFormatException}: args parts must be array`)
975
+ }
976
+
977
+ return _htmlToPlainText(html, args[0])
978
+ }
979
+
980
+ function _htmlToPlainText(html, delimiter = '\n') {
981
+ if (typeof delimiter !== 'string') {
982
+ delimiter = '\n'; // Fallback to default if not a string
983
+ }
984
+
985
+ // First decode HTML entities and normalize whitespace
986
+ const decodedHtml = html
987
+ .replace(/ /g, ' ')
988
+ .replace(/\s+/g, ' '); // Collapse all whitespace to single spaces
989
+
990
+ // Process HTML tags
991
+ let text = decodedHtml
992
+ // Replace block tags with temporary marker (~~~)
993
+ .replace(/<\/?(p|div|h[1-6]|ul|ol|li|pre|section|article|table|tr|td|th)(\s[^>]*)?>/gi, '~~~')
994
+ // Replace <br> tags with temporary marker (~~~)
995
+ .replace(/<br\s*\/?>/gi, '~~~')
996
+ // Remove all other tags
997
+ .replace(/<[^>]+>/g, '')
998
+ // Convert markers to specified delimiter
999
+ .replace(/~~~+/g, delimiter)
1000
+ // Trim and clean whitespace
1001
+ .trim();
1002
+
1003
+ // Special handling for empty delimiter
1004
+ if (delimiter === '') {
1005
+ // Collapse all whitespace to single space
1006
+ text = text.replace(/\s+/g, ' ');
1007
+ } else {
1008
+
1009
+
1010
+ // Collapse multiple delimiters to single
1011
+ text = text.replace(new RegExp(`${escapeRegExp(delimiter)}+`, 'g'), delimiter);
1012
+ // Remove leading/trailing delimiters
1013
+ text = text.replace(new RegExp(`^${escapeRegExp(delimiter)}|${escapeRegExp(delimiter)}$`, 'g'), '');
1014
+ }
1015
+
1016
+ return text;
1017
+ }
1018
+
1019
+
1020
+ function escapeRegExp(string) {
1021
+ return string.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
1022
+ }
1023
+
1024
+
1025
+
892
1026
  ;// ./lib/models/templateCompiler/helpers/_toLowerCase.js
893
1027
 
894
1028
 
@@ -941,6 +1075,8 @@ function _toUpperCase(data, args) {
941
1075
 
942
1076
 
943
1077
 
1078
+
1079
+
944
1080
 
945
1081
 
946
1082
 
@@ -968,6 +1104,9 @@ class TemplateCompiler {
968
1104
  static concatIf(data, args) {
969
1105
  return _concatIf(data, args)
970
1106
  }
1107
+ static divide(data, args) {
1108
+ return _divide(data, args)
1109
+ }
971
1110
  static eq(data, args) {
972
1111
  return _eq(data, args)
973
1112
  }
@@ -979,7 +1118,6 @@ class TemplateCompiler {
979
1118
  static formatDate(data, args) {
980
1119
  return _formatDate(data, args)
981
1120
  }
982
-
983
1121
  static get(data, key, failover = null) {
984
1122
  return _get(data, key, failover)
985
1123
  }
@@ -1010,6 +1148,9 @@ class TemplateCompiler {
1010
1148
  static neq(data, args) {
1011
1149
  return _neq(data, args)
1012
1150
  }
1151
+ static removeHtml(data, args) {
1152
+ return _removeHtml(data, args)
1153
+ }
1013
1154
  static toLowerCase(data, args) {
1014
1155
  return _toLowerCase(data, args)
1015
1156
  }
@@ -1019,6 +1160,9 @@ class TemplateCompiler {
1019
1160
  static parseFunction(expression) {
1020
1161
  return _parseFunction(expression, _FN_NAMES)
1021
1162
  }
1163
+ static parseParams(parameters) {
1164
+ return _parseParams(parameters)
1165
+ }
1022
1166
 
1023
1167
  pipe(expression = '') {
1024
1168
  this.delimiters = expression.substring(0, 2) === '{{' ? TAGS_HANDLEBAR : TAGS_EJS
@@ -1101,6 +1245,10 @@ function _parseSinglePart(input) {
1101
1245
  // 去掉双引号,返回
1102
1246
  return input.substring(1, input.length - 1)
1103
1247
  }
1248
+ if (input.startsWith("'") && input.endsWith("'")) {
1249
+ // 去掉双引号,返回
1250
+ return input.substring(1, input.length - 1)
1251
+ }
1104
1252
 
1105
1253
  const _input = _toBasicType(input)
1106
1254
 
@@ -1128,6 +1276,10 @@ function _toBasicType(input) {
1128
1276
  // 去掉双引号,返回
1129
1277
  return input.substring(1, input.length - 1)
1130
1278
  }
1279
+ if (input.startsWith("'") && input.endsWith("'")) {
1280
+ // 去掉双引号,返回
1281
+ return input.substring(1, input.length - 1)
1282
+ }
1131
1283
  if (input === 'true') {
1132
1284
  return true
1133
1285
  }
@@ -1150,8 +1302,20 @@ function _callFunction(data, functionName, parameters) {
1150
1302
  try {
1151
1303
  let failover
1152
1304
  switch (functionName) {
1305
+ case 'concatIf':
1306
+ return _concatIf(data, parameters)
1307
+ case 'divide':
1308
+ return _divide(data, parameters)
1309
+ case 'eq':
1310
+ return _eq(data, parameters)
1153
1311
  case 'exec':
1154
1312
  return _exec(data, parameters)
1313
+ case 'filterAll':
1314
+ return _filterAll(data, parameters)
1315
+ case 'filterOne':
1316
+ return _filterOne(data, parameters)
1317
+ case 'formatDate':
1318
+ return _formatDate(data, parameters)
1155
1319
  case 'get':
1156
1320
  if (parameters.length > 2) {
1157
1321
  throw new TemplateCompilerException(TEMPLATE_COMPILER_EXCEPTION_TYPE.argumentFormatException)
@@ -1160,34 +1324,26 @@ function _callFunction(data, functionName, parameters) {
1160
1324
  failover = parameters[parameters.length - 1]
1161
1325
  }
1162
1326
  return _get(data, parameters[0], failover)
1163
- case 'join':
1164
- return _join(data, parameters[0])
1165
- case 'map':
1166
- return _map(data, parameters)
1167
- case 'concatIf':
1168
- return _concatIf(data, parameters)
1169
- case 'filterOne':
1170
- return _filterOne(data, parameters)
1171
- case 'filterAll':
1172
- return _filterAll(data, parameters)
1173
- case 'formatDate':
1174
- return _formatDate(data, parameters)
1175
- case 'eq':
1176
- return _eq(data, parameters)
1177
- case 'neq':
1178
- return _neq(data, parameters)
1179
1327
  case 'gt':
1180
1328
  return _gt(data, parameters)
1181
1329
  case 'gte':
1182
1330
  return _gte(data, parameters)
1183
- case 'lt':
1184
- return _lt(data, parameters)
1185
- case 'lte':
1186
- return _lte(data, parameters)
1187
1331
  case 'isEmpty':
1188
1332
  return _isEmpty(data, parameters)
1189
1333
  case 'isNotEmpty':
1190
1334
  return _isNotEmpty(data, parameters)
1335
+ case 'join':
1336
+ return _join(data, parameters[0])
1337
+ case 'lt':
1338
+ return _lt(data, parameters)
1339
+ case 'lte':
1340
+ return _lte(data, parameters)
1341
+ case 'map':
1342
+ return _map(data, parameters)
1343
+ case 'neq':
1344
+ return _neq(data, parameters)
1345
+ case 'removeHtml':
1346
+ return _removeHtml(data, parameters)
1191
1347
  case 'toLowerCase':
1192
1348
  return _toLowerCase(data)
1193
1349
  case 'toUpperCase':
@@ -1336,42 +1492,59 @@ const objectHelper = {
1336
1492
  },
1337
1493
  merge,
1338
1494
  set(obj, path, value) {
1339
- const parts = path.split('.')
1340
- let current = obj
1495
+ const parts = path.split('.');
1496
+ let current = obj;
1497
+
1498
+ // 处理所有中间部分
1341
1499
  for (let i = 0; i < parts.length - 1; i++) {
1342
- const part = parts[i]
1343
- if (part.endsWith('[]')) {
1344
- // 处理数组遍历
1345
- const key = part.slice(0, -2) // 去掉 '[]' 得到属性名
1346
- if (Array.isArray(current[key])) {
1347
- current[key].forEach((item) => set(item, parts.slice(i + 1).join('.'), value))
1500
+ const part = parts[i];
1501
+ let key, index;
1502
+
1503
+ // 检查是否是数组索引格式,如key[0]
1504
+ const arrayMatch = part.match(/^(\w+)\[(\d+)\]$/);
1505
+ if (arrayMatch) {
1506
+ key = arrayMatch[1];
1507
+ index = parseInt(arrayMatch[2], 10);
1508
+ // 确保当前层级的数组存在
1509
+ if (!current[key] || !Array.isArray(current[key])) {
1510
+ current[key] = [];
1348
1511
  }
1349
- return // 处理完数组后直接返回
1350
- }
1351
- if (part.includes('[') && part.includes(']')) {
1352
- // 处理数组索引
1353
- const arrayMatch = part.match(/(\w+)\[(\d+)\]/)
1354
- if (arrayMatch) {
1355
- const key = arrayMatch[1]
1356
- const index = arrayMatch[2]
1357
- if (Array.isArray(current[key]) && current[key][index]) {
1358
- current = current[key][index]
1359
- } else {
1360
- return // 如果数组或索引不存在,直接返回
1361
- }
1512
+ // 扩展数组到足够大
1513
+ while (current[key].length <= index) {
1514
+ current[key].push(undefined);
1515
+ }
1516
+ // 如果当前位置未定义或为null,初始化为对象
1517
+ if (current[key][index] == null) {
1518
+ current[key][index] = {};
1362
1519
  }
1520
+ current = current[key][index];
1363
1521
  } else {
1364
1522
  // 处理普通属性
1365
1523
  if (!current[part]) {
1366
- current[part] = {} // 如果属性不存在,创建一个空对象
1524
+ current[part] = {};
1367
1525
  }
1368
- current = current[part]
1526
+ current = current[part];
1369
1527
  }
1370
1528
  }
1371
-
1372
- // 设置最终属性值
1373
- const lastPart = parts[parts.length - 1]
1374
- current[lastPart] = value
1529
+
1530
+ // 处理最后一部分
1531
+ const lastPart = parts[parts.length - 1];
1532
+ const arrayMatch = lastPart.match(/^(\w+)\[(\d+)\]$/);
1533
+ if (arrayMatch) {
1534
+ const key = arrayMatch[1];
1535
+ const index = parseInt(arrayMatch[2], 10);
1536
+ // 确保数组存在
1537
+ if (!current[key] || !Array.isArray(current[key])) {
1538
+ current[key] = [];
1539
+ }
1540
+ // 扩展数组到所需索引
1541
+ while (current[key].length <= index) {
1542
+ current[key].push(undefined);
1543
+ }
1544
+ current[key][index] = value;
1545
+ } else {
1546
+ current[lastPart] = value;
1547
+ }
1375
1548
  }
1376
1549
  }
1377
1550
 
@@ -1439,113 +1612,728 @@ async function pReduce(iterable, reducer, initialValue) {
1439
1612
 
1440
1613
 
1441
1614
 
1442
- ;// ./lib/models/apiResponse/apiResponse.js
1443
- class ApiResponse {
1444
- constructor(options = {}) {
1615
+ ;// ./lib/models/repo/repo.js
1616
+
1617
+
1618
+ class Repo {
1619
+ constructor(options) {
1445
1620
  options = options || {}
1446
- this._data = options.data || options._data || []
1447
- this.err = options.err
1448
- this.isNew = options.isNew || false
1449
- this.message = options.message
1450
- this.total = options.total || 0
1451
- this._instanceBuilder = options._instanceBuilder
1621
+ this.model = options.model
1622
+ this._sharedOptions = options._sharedOptions // { session: this.dbTransaction }
1623
+ this._queryOptions = options._queryOptions
1624
+ this._saveOptions = options._saveOptions
1625
+ this._Class = options._constructor && options._constructor._Class
1626
+ ? options._constructor._Class
1627
+ : null
1452
1628
  }
1453
-
1454
1629
  static init(options = {}) {
1455
- if (options instanceof this) {
1456
- return options
1457
- }
1458
- const instance = new this(options)
1459
- return instance
1630
+ return init(this, options)
1460
1631
  }
1461
1632
  static get _classname() {
1462
- return 'ApiResponse'
1633
+ return 'Repo'
1463
1634
  }
1464
1635
  static get _superclass() {
1465
- return 'ApiResponse'
1636
+ return 'Repo'
1466
1637
  }
1467
1638
 
1468
- // getters
1469
- get data() {
1470
- if (this._instanceBuilder && (typeof this._instanceBuilder === 'function')) {
1471
- return this._data.map(this._instanceBuilder)
1472
- }
1473
- return this._data
1639
+ get _classname() {
1640
+ return 'Repo'
1474
1641
  }
1475
- }
1476
-
1477
1642
 
1643
+ get _superclass() {
1644
+ return 'Repo'
1645
+ }
1478
1646
 
1479
- ;// ./lib/models/apiResponse/makeApiResponse.js
1480
-
1647
+ get isValid() {
1648
+ return this.model
1649
+ && (typeof this.model.deleteOne === 'function')
1650
+ && (typeof this.model.findAll === 'function')
1651
+ && (typeof this.model.saveOne === 'function')
1652
+ }
1481
1653
 
1482
- function makeApiResponse({ repo, result }) {
1483
- return ApiResponse.init({
1484
- ...result,
1485
- _instanceBuilder: (i) => {
1486
- return repo.init(i)
1654
+ get queryOptions() {
1655
+ return {
1656
+ ...this._sharedOptions,
1657
+ ...this._queryOptions,
1487
1658
  }
1488
- })
1489
- }
1490
-
1491
-
1492
-
1493
- ;// ./lib/models/apiResponse/index.js
1494
-
1495
-
1496
-
1497
-
1498
-
1499
- ;// ./lib/models/keyValueObject/keyValueObject.js
1500
- class KeyValueObject {
1501
- constructor(options = {}) {
1502
- options = options || {}
1503
- this.key = options.key || null
1504
- this.value = (typeof options.value !== 'undefined') ? options.value : ''
1505
1659
  }
1506
1660
 
1507
- // Class methods
1508
- static init(options = {}) {
1509
- if (options instanceof this) {
1510
- return options
1661
+ get saveOptions() {
1662
+ return {
1663
+ ...this._sharedOptions,
1664
+ ...this._saveOptions,
1511
1665
  }
1512
- const instance = new this(options)
1513
- return instance.isValid ? instance : null
1514
1666
  }
1515
- static initFromArray(arr = []) {
1516
- if (Array.isArray(arr)) {
1517
- return arr.map((a) => this.init(a))
1667
+
1668
+ init(options) {
1669
+ if (this._Class && typeof this._Class.init === 'function') {
1670
+ return this._Class.init(options)
1518
1671
  }
1519
- return []
1520
- }
1521
- static initOnlyValidFromArray(arr = []) {
1522
- return this.initFromArray(arr).filter((i) => i)
1523
- }
1524
- static get _classname() {
1525
- return 'KeyValueObject'
1526
- }
1527
- static get _superclass() {
1528
- return 'KeyValueObject'
1672
+ return options
1529
1673
  }
1530
1674
 
1531
- static addItem(arr, key, value) {
1532
- arr.push(this.init({ key, value }))
1533
- }
1534
- static addRecord(arr = [], key, value) {
1535
- if (!this.hasKeyValue(arr, key, value)) {
1536
- arr.push(this.init({ key, value }))
1675
+ async deleteOne({ id }) {
1676
+ try {
1677
+ const result = await this.model.deleteOne({ _id: id })
1678
+ return {
1679
+ ...result, // { message: 'ok', total }
1680
+ isNew: false,
1681
+ data: []
1682
+ }
1683
+ } catch (err) {
1684
+ throw err
1537
1685
  }
1538
- return arr
1539
1686
  }
1540
- static appendRecord(arr = [], key, value) {
1541
- return arr.map((item) => {
1542
- if (this.sameKey(item, key)) {
1543
- item.value = [...item.value, ...value]
1544
- }
1545
- return item
1687
+
1688
+ // systemLog is optional
1689
+ findAll({ query, systemLog }) {
1690
+ const log = _makeLog({
1691
+ systemLog,
1692
+ label: 'REPO_READ',
1693
+ message: `fn ${this._classname}.prototype.findAll`,
1694
+ input: [{ query: { ...query }, systemLog: { ...systemLog } }]
1546
1695
  })
1547
- }
1548
- static appendValueArray(arr = [], key, value) {
1696
+ return new Promise((resolve, reject) => {
1697
+ this.model.findAll(query, this.queryOptions, (err, data, total) => {
1698
+ if (err) {
1699
+ log({ level: 'warn', output: err.toString() })
1700
+ reject(err)
1701
+ } else {
1702
+ const result = {
1703
+ isNew: false,
1704
+ data,
1705
+ total: total || data.length
1706
+ }
1707
+ log({ level: 'info', output: { ...result } })
1708
+ resolve(result)
1709
+ }
1710
+ })
1711
+ })
1712
+ }
1713
+
1714
+ findOne({ query, systemLog }) {
1715
+ const log = _makeLog({
1716
+ systemLog,
1717
+ label: 'REPO_READ',
1718
+ message: `fn ${this._classname}.prototype.findOne`,
1719
+ input: [{ query: { ...query }, systemLog: { ...systemLog } }]
1720
+ })
1721
+ return new Promise((resolve, reject) => {
1722
+ this.model.findAll(query, this.queryOptions, (err, data) => {
1723
+ if (err) {
1724
+ reject(err)
1725
+ } else if (data.length === 1) {
1726
+ const result = {
1727
+ isNew: false,
1728
+ data,
1729
+ total: 1
1730
+ }
1731
+ log({ level: 'info', output: { ...result } })
1732
+ resolve(result)
1733
+ } else if (data.length === 0) {
1734
+ reject(new Error('record not found'))
1735
+ } else {
1736
+ reject(new Error('more than one is found'))
1737
+ }
1738
+ })
1739
+ })
1740
+ .catch((err) => {
1741
+ log({ level: 'warn', output: err.toString() })
1742
+ throw err
1743
+ })
1744
+ }
1745
+
1746
+ saveAll({ docs, systemLog }) {
1747
+ let isNew
1748
+ const log = _makeLog({
1749
+ systemLog,
1750
+ label: 'REPO_WRITE',
1751
+ message: `fn ${this._classname}.prototype.saveAll`,
1752
+ input: [{ docs: [...docs], systemLog: { ...systemLog } }]
1753
+ })
1754
+ const promise = typeof this.model.saveAll === 'function'
1755
+ ? this.model.saveAll({ docs })
1756
+ : Promise.all(docs.map(async (doc) => {
1757
+ if (doc) {
1758
+ const result = await this.saveOne({ doc })
1759
+ isNew = result.isNew
1760
+ const _data = result._data || result.data
1761
+ return _data[0]
1762
+ }
1763
+ return null
1764
+ }))
1765
+ return promise.then((savedData) => {
1766
+ if (savedData.length !== 1) isNew = null
1767
+ const result = {
1768
+ data: savedData,
1769
+ isNew,
1770
+ total: savedData.length
1771
+ }
1772
+ log({ level: 'info', output: { ...result } })
1773
+ return result
1774
+ }).catch((err) => {
1775
+ log({ level: 'warn', output: err.toString() })
1776
+ throw err
1777
+ })
1778
+ }
1779
+
1780
+ saveOne({ doc, systemLog }) {
1781
+ const log = _makeLog({
1782
+ systemLog,
1783
+ label: 'REPO_WRITE',
1784
+ message: `fn ${this._classname}.prototype.saveOne`,
1785
+ input: [{ doc: { ...doc }, systemLog: { ...systemLog } }]
1786
+ })
1787
+ return new Promise((resolve, reject) => {
1788
+ this.model.saveOne(doc, this.saveOptions, (err, result) => {
1789
+ if (err) {
1790
+ log({ level: 'warn', output: err.toString() })
1791
+ reject(err)
1792
+ } else {
1793
+ log({ level: 'info', output: { ...result } })
1794
+ resolve(result)
1795
+ }
1796
+ })
1797
+ })
1798
+ }
1799
+ }
1800
+
1801
+ function _makeLog({ systemLog, label, message: message1, input } = {}) {
1802
+ return ({ level, messgae: massage2, output } = {}) => {
1803
+ if (systemLog && systemLog.systemLogHelper) {
1804
+ systemLog.systemLogHelper.log({
1805
+ batchId: systemLog.batchId,
1806
+ label,
1807
+ level,
1808
+ message: massage2 || message1,
1809
+ data: {
1810
+ payload: {
1811
+ input,
1812
+ output
1813
+ }
1814
+ }
1815
+ })
1816
+ }
1817
+ }
1818
+ }
1819
+
1820
+
1821
+
1822
+ ;// ./lib/models/repo/index.js
1823
+
1824
+
1825
+
1826
+
1827
+ ;// ./lib/models/apiResponse/apiResponse.js
1828
+ class ApiResponse {
1829
+ constructor(options = {}) {
1830
+ options = options || {}
1831
+ this._data = options.data || options._data || []
1832
+ this.err = options.err
1833
+ this.isNew = options.isNew || false
1834
+ this.message = options.message
1835
+ this.total = options.total || 0
1836
+ this._instanceBuilder = options._instanceBuilder
1837
+ }
1838
+
1839
+ static init(options = {}) {
1840
+ if (options instanceof this) {
1841
+ return options
1842
+ }
1843
+ const instance = new this(options)
1844
+ return instance
1845
+ }
1846
+ static get _classname() {
1847
+ return 'ApiResponse'
1848
+ }
1849
+ static get _superclass() {
1850
+ return 'ApiResponse'
1851
+ }
1852
+
1853
+ // getters
1854
+ get data() {
1855
+ if (this._instanceBuilder && (typeof this._instanceBuilder === 'function')) {
1856
+ return this._data.map(this._instanceBuilder)
1857
+ }
1858
+ return this._data
1859
+ }
1860
+ }
1861
+
1862
+
1863
+
1864
+ ;// ./lib/models/apiResponse/makeApiResponse.js
1865
+
1866
+
1867
+ function makeApiResponse({ repo, result }) {
1868
+ return ApiResponse.init({
1869
+ ...result,
1870
+ _instanceBuilder: (i) => {
1871
+ return repo.init(i)
1872
+ }
1873
+ })
1874
+ }
1875
+
1876
+
1877
+
1878
+ ;// ./lib/models/service/service.js
1879
+
1880
+
1881
+
1882
+ class Service {
1883
+ constructor({ repo }) {
1884
+ this.repo = repo
1885
+ }
1886
+
1887
+ static get _classname() {
1888
+ return 'Service'
1889
+ }
1890
+ static get _superclass() {
1891
+ return 'Service'
1892
+ }
1893
+
1894
+ deleteOne({ id }) {
1895
+ return this.repo.deleteOne({ id })
1896
+ .catch(() => {
1897
+ throw new Error(`Not found for query: ${id}`)
1898
+ })
1899
+ }
1900
+
1901
+ async findAll({ query = {}, systemLog } = {}) {
1902
+ const result = await this.repo.findAll({ query, systemLog })
1903
+ return makeApiResponse({
1904
+ repo: this.repo,
1905
+ result
1906
+ })
1907
+ }
1908
+
1909
+ async findOne({ query = {}, systemLog } = {}) {
1910
+ const result = await this.repo.findOne({ query, systemLog })
1911
+ return makeApiResponse({
1912
+ repo: this.repo,
1913
+ result
1914
+ })
1915
+ }
1916
+
1917
+ init(options) {
1918
+ return this.repo.init(options)
1919
+ }
1920
+ initFromArray(arr = []) {
1921
+ if (Array.isArray(arr)) {
1922
+ return arr.map((a) => this.init(a))
1923
+ }
1924
+ return []
1925
+ }
1926
+ initOnlyValidFromArray(arr = []) {
1927
+ return this.initFromArray(arr).filter((i) => i)
1928
+ }
1929
+
1930
+ async saveAll({ docs = [], config = {}, systemLog } = {}) {
1931
+ const copies = docs.map((doc) => {
1932
+ return config.skipInit ? doc : this.init(doc)
1933
+ })
1934
+ const result = await this.repo.saveAll({ docs: copies, systemLog })
1935
+ return makeApiResponse({
1936
+ repo: this.repo,
1937
+ result
1938
+ })
1939
+ }
1940
+
1941
+ // set skipInit to true if we want to use POST for query
1942
+ async saveOne({ doc = {}, config = {}, systemLog } = {}) {
1943
+ const copy = config.skipInit ? doc : this.init(doc)
1944
+ if (copy) {
1945
+ const result = await this.repo.saveOne({ doc: copy, systemLog })
1946
+ return makeApiResponse({
1947
+ repo: this.repo,
1948
+ result
1949
+ })
1950
+ }
1951
+ return {
1952
+ isNew: null,
1953
+ data: [],
1954
+ err: new Error('doc is not a valid instance')
1955
+ }
1956
+ }
1957
+ }
1958
+
1959
+ function makeService({ repo }) {
1960
+ if (repo === undefined) {
1961
+ throw new Error('repo is required.')
1962
+ }
1963
+ if (repo._superclass !== Repo._superclass) {
1964
+ throw new Error('repo is not an instance of Repo.')
1965
+ }
1966
+ return new Service({ repo })
1967
+ }
1968
+
1969
+
1970
+
1971
+ ;// ./lib/models/service/index.js
1972
+
1973
+
1974
+
1975
+
1976
+ ;// ./lib/helpers/generalPost/generalPost.js
1977
+
1978
+
1979
+
1980
+
1981
+
1982
+ async function generalPost({ body = {}, GeneralModel, UniqueKeyGenerator, resourceInfo }) {
1983
+ const { resources, data, globalShared = {}, shared = {}, relationship = {} } = body
1984
+ const _resourceInfo = resourceInfo || body.resourceInfo
1985
+ _attachShared(data, globalShared, shared)
1986
+ const obj = await pReduce(resources, async (acc, resource) => {
1987
+ const service = _makeService(resource, _resourceInfo, UniqueKeyGenerator, GeneralModel)
1988
+ _createRelationship(data, relationship[resource], acc)
1989
+ const _data = data[resource]
1990
+ const result = await service.saveAll({ docs: [].concat(_data) })
1991
+ acc[resource] = Array.isArray(_data) ? result._data : result._data[0]
1992
+ return acc
1993
+ }, {})
1994
+ return obj
1995
+ }
1996
+
1997
+ function _attachShared(data, globalShared = {}, shared = {}) {
1998
+ Object.keys(shared).forEach((key) => {
1999
+ const _data = data[key]
2000
+ if (Array.isArray(_data)) {
2001
+ data[key] = _data.map((_dataItem) => {
2002
+ return objectHelper.merge({}, _dataItem, globalShared, shared[key] || {})
2003
+ })
2004
+ } else {
2005
+ data[key] = objectHelper.merge({}, _data, globalShared, shared[key] || {})
2006
+ }
2007
+ })
2008
+ }
2009
+
2010
+ function _createRelationship(data, relationship = {}, object) {
2011
+ Object.keys(relationship).forEach((key) => {
2012
+ const path = relationship[key]
2013
+ const val = objectHelper.get(object, path)
2014
+ objectHelper.set(data, key, val)
2015
+ })
2016
+ }
2017
+
2018
+ function _makeService(resource, resourceInfo, UniqueKeyGenerator, GeneralModel) {
2019
+ const { collectionName, fields } = resourceInfo[resource]
2020
+ const uniqueKeyGenerator = UniqueKeyGenerator.makeGenerator(fields)
2021
+ const model = new GeneralModel({ collectionName, uniqueKeyGenerator })
2022
+ return makeService({
2023
+ repo: new Repo({ model })
2024
+ })
2025
+ }
2026
+
2027
+
2028
+
2029
+ ;// ./lib/helpers/generalPost/index.js
2030
+
2031
+
2032
+
2033
+
2034
+ ;// ./lib/helpers/init/init.js
2035
+ function init(_class, options) {
2036
+ if (options instanceof _class) {
2037
+ return options
2038
+ }
2039
+ try {
2040
+ const instance = new _class(options)
2041
+ return instance.isValid !== false ? instance : null
2042
+ } catch (e) {
2043
+ console.log(`init failed for class: ${_class._classname || 'no _classname'}`, e)
2044
+ return null
2045
+ }
2046
+ }
2047
+
2048
+ ;// ./lib/helpers/init/index.js
2049
+
2050
+
2051
+ ;// ./lib/helpers/initFromArray/initFromArray.js
2052
+
2053
+
2054
+ function initFromArray(_class, arr) {
2055
+ if (Array.isArray(arr)) {
2056
+ return arr.map((a) => init(_class, a))
2057
+ }
2058
+ return []
2059
+ }
2060
+
2061
+ ;// ./lib/helpers/initFromArray/index.js
2062
+
2063
+
2064
+ ;// ./lib/helpers/initOnlyValidFromArray/initOnlyValidFromArray.js
2065
+
2066
+
2067
+ function initOnlyValidFromArray(_class, arr) {
2068
+ return initFromArray(_class, arr).filter((i) => i)
2069
+ }
2070
+
2071
+ ;// ./lib/helpers/initOnlyValidFromArray/index.js
2072
+
2073
+
2074
+ ;// ./lib/helpers/padZeros/padZeros.js
2075
+ function padZeros(num, minLength = 6) {
2076
+ num = num.toString()
2077
+ if (num.length < minLength) {
2078
+ return padZeros('0' + num, minLength)
2079
+ }
2080
+ return num
2081
+ }
2082
+
2083
+
2084
+
2085
+ ;// ./lib/helpers/padZeros/index.js
2086
+
2087
+
2088
+
2089
+
2090
+ ;// ./lib/helpers/pReduce/index.js
2091
+
2092
+
2093
+
2094
+
2095
+ ;// ./lib/helpers/stringFormatter/stringFormatter.js
2096
+ function stringFormatter(str, delimiter = '_') {
2097
+ if (str === null || typeof str === 'undefined' || typeof str.toString === 'undefined') {
2098
+ return null
2099
+ }
2100
+ return str.toString()
2101
+ .trim()
2102
+ .toUpperCase()
2103
+ .replace('-', delimiter)
2104
+ .replace(' ', delimiter)
2105
+ }
2106
+
2107
+
2108
+
2109
+ ;// ./lib/helpers/stringFormatter/index.js
2110
+
2111
+
2112
+
2113
+
2114
+ ;// ./lib/helpers/stringHelper/stringHelper.js
2115
+ function baseXEncode(num, base = 34) {
2116
+ const charset = getBaseCharset(base)
2117
+ return encode(num, charset)
2118
+ }
2119
+
2120
+ function encode(int, charset) {
2121
+ const { byCode } = charset
2122
+ if (int === 0) {
2123
+ return byCode[0]
2124
+ }
2125
+
2126
+ let res = ''
2127
+ const max = charset.length
2128
+ while (int > 0) {
2129
+ res = byCode[int % max] + res
2130
+ int = Math.floor(int / max)
2131
+ }
2132
+ return res
2133
+ }
2134
+
2135
+ function getBaseCharset(base) {
2136
+ let charset = '9876543210ABCDEFGHJKLMNPQRSTUVWXYZ'
2137
+ if (base === 58) {
2138
+ charset = '9876543210ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnpqrstuvwxyz'
2139
+ }
2140
+ return indexCharset(charset)
2141
+ }
2142
+
2143
+ function indexCharset(str) {
2144
+ const byCode = {}
2145
+ const byChar = {}
2146
+ const { length } = str
2147
+ let char
2148
+ for (let i = 0; i < length; i++) {
2149
+ char = str[i]
2150
+ byCode[i] = char
2151
+ byChar[char] = i;
2152
+ }
2153
+ return { byCode, byChar, length }
2154
+ }
2155
+
2156
+ function isSame(str1, str2) {
2157
+ if (typeof str1 !== 'string' || typeof str2 !== 'string') {
2158
+ return false
2159
+ }
2160
+ return str1.trim().toUpperCase() === str2.trim().toUpperCase()
2161
+ }
2162
+
2163
+ function randomString({ len = 16, pattern = 'a1' } = {}) {
2164
+ const A = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'
2165
+ const a = 'abcdefghijklmnopqrstuvwxyz'
2166
+ const num = '1234567890'
2167
+ const mark = '~!@#$%^&*_+-='
2168
+ let str = ''
2169
+ if (pattern.includes('A')) {
2170
+ str += A
2171
+ }
2172
+ if (pattern.includes('a')) {
2173
+ str += a
2174
+ }
2175
+ if (pattern.includes('1')) {
2176
+ str += num
2177
+ }
2178
+ if (pattern.includes('#')) {
2179
+ str += mark
2180
+ }
2181
+ const chars = [...str]
2182
+ return [...Array(len)].map(i => {
2183
+ return chars[(Math.random() * chars.length) | 0]
2184
+ }).join``
2185
+ }
2186
+
2187
+ function reverse(str) {
2188
+ const _str = (typeof str !== 'string') ? str.toString() : str
2189
+ const splitString = _str.split('')
2190
+ const reverseArray = splitString.reverse()
2191
+ return reverseArray.join('')
2192
+ }
2193
+
2194
+ function setCode(base = 34) {
2195
+ const now = (new Date()).valueOf()
2196
+ const random = randomString({
2197
+ len: 8,
2198
+ pattern: '1'
2199
+ })
2200
+ const str = reverse(`${now}${random}`)
2201
+ // const str = `${now}${random}`
2202
+ return baseXEncode(str, base)
2203
+ }
2204
+
2205
+ function toCamelCase(str) {
2206
+ if (!str) return ''
2207
+ return str
2208
+ .trim()
2209
+ .split(/\s+/)
2210
+ .map((word, index) => {
2211
+ if (!word) return ''
2212
+ if (index === 0) {
2213
+ return word.toLowerCase()
2214
+ }
2215
+ return word.charAt(0).toUpperCase() + word.slice(1).toLowerCase()
2216
+ })
2217
+ .join('')
2218
+ }
2219
+
2220
+ const stringHelper = {
2221
+ isSame,
2222
+ setCode,
2223
+ toCamelCase,
2224
+ }
2225
+
2226
+
2227
+
2228
+ ;// ./lib/helpers/stringHelper/index.js
2229
+
2230
+
2231
+
2232
+
2233
+ ;// ./lib/helpers/trackingPlugin/trackingPlugin.js
2234
+ function trackingPlugin(schema, options) {
2235
+ // Add meta fields
2236
+ schema.add({
2237
+ meta: {
2238
+ active: { type: Boolean, default: true },
2239
+ created: { type: Number },
2240
+ creator: { type: String },
2241
+ deleted: { type: Boolean, default: false },
2242
+ modified: { type: Number },
2243
+ owner: { type: String },
2244
+ }
2245
+ })
2246
+
2247
+ // Auto-update hook
2248
+ schema.pre('save', function(next) {
2249
+ this.meta.modified = Date.now()
2250
+ next()
2251
+ })
2252
+
2253
+ // Optional: Add helper methods
2254
+ // schema.methods.touch = function(userId) {
2255
+ // this.meta.updatedAt = new Date()
2256
+ // this.meta.updatedBy = userId
2257
+ // }
2258
+ }
2259
+
2260
+ ;// ./lib/helpers/trackingPlugin/index.js
2261
+
2262
+
2263
+ ;// ./lib/helpers/index.js
2264
+
2265
+
2266
+
2267
+
2268
+
2269
+
2270
+
2271
+
2272
+
2273
+
2274
+
2275
+
2276
+
2277
+
2278
+
2279
+
2280
+
2281
+ ;// ./lib/models/apiResponse/index.js
2282
+
2283
+
2284
+
2285
+
2286
+
2287
+ ;// ./lib/models/keyValueObject/keyValueObject.js
2288
+ class KeyValueObject {
2289
+ constructor(options = {}) {
2290
+ options = options || {}
2291
+ this.key = options.key || null
2292
+ this.value = (typeof options.value !== 'undefined') ? options.value : ''
2293
+ }
2294
+
2295
+ // Class methods
2296
+ static init(options = {}) {
2297
+ if (options instanceof this) {
2298
+ return options
2299
+ }
2300
+ const instance = new this(options)
2301
+ return instance.isValid ? instance : null
2302
+ }
2303
+ static initFromArray(arr = []) {
2304
+ if (Array.isArray(arr)) {
2305
+ return arr.map((a) => this.init(a))
2306
+ }
2307
+ return []
2308
+ }
2309
+ static initOnlyValidFromArray(arr = []) {
2310
+ return this.initFromArray(arr).filter((i) => i)
2311
+ }
2312
+ static get _classname() {
2313
+ return 'KeyValueObject'
2314
+ }
2315
+ static get _superclass() {
2316
+ return 'KeyValueObject'
2317
+ }
2318
+
2319
+ static addItem(arr, key, value) {
2320
+ arr.push(this.init({ key, value }))
2321
+ }
2322
+ static addRecord(arr = [], key, value) {
2323
+ if (!this.hasKeyValue(arr, key, value)) {
2324
+ arr.push(this.init({ key, value }))
2325
+ }
2326
+ return arr
2327
+ }
2328
+ static appendRecord(arr = [], key, value) {
2329
+ return arr.map((item) => {
2330
+ if (this.sameKey(item, key)) {
2331
+ item.value = [...item.value, ...value]
2332
+ }
2333
+ return item
2334
+ })
2335
+ }
2336
+ static appendValueArray(arr = [], key, value) {
1549
2337
  return arr.map((item) => {
1550
2338
  if (this.sameKey(item, key)) {
1551
2339
  item.value = [...item.value, ...value]
@@ -1719,20 +2507,6 @@ function _isSame(key1, key2) {
1719
2507
 
1720
2508
 
1721
2509
 
1722
- ;// ./lib/helpers/stringFormatter/stringFormatter.js
1723
- function stringFormatter(str, delimiter = '_') {
1724
- if (str === null || typeof str === 'undefined' || typeof str.toString === 'undefined') {
1725
- return null
1726
- }
1727
- return str.toString()
1728
- .trim()
1729
- .toUpperCase()
1730
- .replace('-', delimiter)
1731
- .replace(' ', delimiter)
1732
- }
1733
-
1734
-
1735
-
1736
2510
  ;// ./lib/models/metadata/metadata.js
1737
2511
 
1738
2512
 
@@ -1844,314 +2618,218 @@ class QMeta {
1844
2618
 
1845
2619
 
1846
2620
 
1847
- ;// ./lib/models/repo/repo.js
1848
- class Repo {
1849
- constructor(options) {
2621
+ ;// ./lib/models/trackedEntity/trackedEntity.js
2622
+
2623
+
2624
+ class TrackedEntity {
2625
+ constructor(options = {}) {
1850
2626
  options = options || {}
1851
- this.model = options.model
1852
- this._sharedOptions = options._sharedOptions // { session: this.dbTransaction }
1853
- this._queryOptions = options._queryOptions
1854
- this._saveOptions = options._saveOptions
1855
- this._Class = options._constructor && options._constructor._Class
1856
- ? options._constructor._Class
1857
- : null
1858
- }
1859
- static init(options = {}) {
1860
- if (options instanceof this) {
1861
- return options
2627
+ const timestamp = Date.now()
2628
+ const _tracking = {
2629
+ active: options.active ?? true,
2630
+ created: options.created ?? timestamp,
2631
+ creator: options.creator ?? '',
2632
+ deleted: options.deleted ?? false,
2633
+ modified: options.modified ?? timestamp,
2634
+ owner: options.owner ?? '',
1862
2635
  }
1863
- const instance = new this(options)
1864
- return instance.isValid ? instance : null
2636
+
2637
+ this.meta = { ..._tracking, ...options.meta }
2638
+
2639
+ // if (trackFlat) {
2640
+ // Object.assign(this, _tracking)
2641
+ // } else {
2642
+ // this.meta = { ..._tracking, ...options.meta }
2643
+ // }
1865
2644
  }
2645
+
2646
+ // Class methods
1866
2647
  static get _classname() {
1867
- return 'Repo'
2648
+ return 'TrackedEntity'
1868
2649
  }
1869
2650
  static get _superclass() {
1870
- return 'Repo'
2651
+ return 'TrackedEntity'
1871
2652
  }
1872
2653
 
1873
- get _classname() {
1874
- return 'Repo'
2654
+ static init(options = {}) {
2655
+ return init(this, options)
1875
2656
  }
1876
-
1877
- get _superclass() {
1878
- return 'Repo'
2657
+ static initFromArray(arr = []) {
2658
+ return initFromArray(this, arr)
2659
+ }
2660
+ static initOnlyValidFromArray(arr = []) {
2661
+ return initOnlyValidFromArray(this, arr)
1879
2662
  }
2663
+ // static nest(entity) {
2664
+ // const { active, created, creator, deleted, modified, owner, ...rest } = entity
2665
+ // return { ...rest, meta: { active, created, creator, deleted, modified, owner } }
2666
+ // }
1880
2667
 
2668
+ // getters
1881
2669
  get isValid() {
1882
- return this.model
1883
- && (typeof this.model.deleteOne === 'function')
1884
- && (typeof this.model.findAll === 'function')
1885
- && (typeof this.model.saveOne === 'function')
2670
+ return !!this
1886
2671
  }
1887
-
1888
- get queryOptions() {
1889
- return {
1890
- ...this._sharedOptions,
1891
- ...this._queryOptions,
1892
- }
2672
+ get active() {
2673
+ return this.meta?.active ?? this.active
1893
2674
  }
1894
-
1895
- get saveOptions() {
1896
- return {
1897
- ...this._sharedOptions,
1898
- ...this._saveOptions,
1899
- }
2675
+ get created() {
2676
+ return this.meta?.created ?? this.created
1900
2677
  }
1901
-
1902
- init(options) {
1903
- if (this._Class && typeof this._Class.init === 'function') {
1904
- return this._Class.init(options)
2678
+ get creator() {
2679
+ return this.meta?.creator ?? this.creator
2680
+ }
2681
+ get deleted() {
2682
+ return this.meta?.deleted ?? this.deleted
2683
+ }
2684
+ get modified() {
2685
+ return this.meta?.modified ?? this.modified
2686
+ }
2687
+ get owner() {
2688
+ return this.meta?.owner ?? this.owner
2689
+ }
2690
+ delete() {
2691
+ return this.setDeleted()
2692
+ }
2693
+ setActive() {
2694
+ if (this.meta) {
2695
+ this.meta.active = true
2696
+ } else {
2697
+ this.active = true
1905
2698
  }
1906
- return options
2699
+ return this
1907
2700
  }
1908
-
1909
- async deleteOne({ id }) {
1910
- try {
1911
- const result = await this.model.deleteOne({ _id: id })
1912
- return {
1913
- ...result, // { message: 'ok', total }
1914
- isNew: false,
1915
- data: []
1916
- }
1917
- } catch (err) {
1918
- throw err
2701
+ setDeleted() {
2702
+ if (this.meta) {
2703
+ this.meta.deleted = true
2704
+ } else {
2705
+ this.deleted = true
1919
2706
  }
2707
+ return this
1920
2708
  }
1921
-
1922
- // systemLog is optional
1923
- findAll({ query, systemLog }) {
1924
- const log = _makeLog({
1925
- systemLog,
1926
- label: 'REPO_READ',
1927
- message: `fn ${this._classname}.prototype.findAll`,
1928
- input: [{ query: { ...query }, systemLog: { ...systemLog } }]
1929
- })
1930
- return new Promise((resolve, reject) => {
1931
- this.model.findAll(query, this.queryOptions, (err, data, total) => {
1932
- if (err) {
1933
- log({ level: 'warn', output: err.toString() })
1934
- reject(err)
1935
- } else {
1936
- const result = {
1937
- isNew: false,
1938
- data,
1939
- total: total || data.length
1940
- }
1941
- log({ level: 'info', output: { ...result } })
1942
- resolve(result)
1943
- }
1944
- })
1945
- })
2709
+ setModified() {
2710
+ const timestamp = Date.now()
2711
+ if (this.meta) {
2712
+ this.meta.modified = timestamp
2713
+ } else {
2714
+ this.modified = timestamp
2715
+ }
2716
+ return this
1946
2717
  }
1947
-
1948
- findOne({ query, systemLog }) {
1949
- const log = _makeLog({
1950
- systemLog,
1951
- label: 'REPO_READ',
1952
- message: `fn ${this._classname}.prototype.findOne`,
1953
- input: [{ query: { ...query }, systemLog: { ...systemLog } }]
1954
- })
1955
- return new Promise((resolve, reject) => {
1956
- this.model.findAll(query, this.queryOptions, (err, data) => {
1957
- if (err) {
1958
- reject(err)
1959
- } else if (data.length === 1) {
1960
- const result = {
1961
- isNew: false,
1962
- data,
1963
- total: 1
1964
- }
1965
- log({ level: 'info', output: { ...result } })
1966
- resolve(result)
1967
- } else if (data.length === 0) {
1968
- reject(new Error('record not found'))
1969
- } else {
1970
- reject(new Error('more than one is found'))
1971
- }
1972
- })
1973
- })
1974
- .catch((err) => {
1975
- log({ level: 'warn', output: err.toString() })
1976
- throw err
1977
- })
2718
+ setOwner(owner) {
2719
+ if (!owner) {
2720
+ return this
2721
+ }
2722
+ if (this.meta) {
2723
+ this.meta.owner = owner
2724
+ } else {
2725
+ this.owner = owner
2726
+ }
2727
+ return this
1978
2728
  }
1979
-
1980
- saveAll({ docs, systemLog }) {
1981
- let isNew
1982
- const log = _makeLog({
1983
- systemLog,
1984
- label: 'REPO_WRITE',
1985
- message: `fn ${this._classname}.prototype.saveAll`,
1986
- input: [{ docs: [...docs], systemLog: { ...systemLog } }]
1987
- })
1988
- const promise = typeof this.model.saveAll === 'function'
1989
- ? this.model.saveAll({ docs })
1990
- : Promise.all(docs.map(async (doc) => {
1991
- if (doc) {
1992
- const result = await this.saveOne({ doc })
1993
- isNew = result.isNew
1994
- const _data = result._data || result.data
1995
- return _data[0]
1996
- }
1997
- return null
1998
- }))
1999
- return promise.then((savedData) => {
2000
- if (savedData.length !== 1) isNew = null
2001
- const result = {
2002
- data: savedData,
2003
- isNew,
2004
- total: savedData.length
2005
- }
2006
- log({ level: 'info', output: { ...result } })
2007
- return result
2008
- }).catch((err) => {
2009
- log({ level: 'warn', output: err.toString() })
2010
- throw err
2011
- })
2729
+ unsetActive() {
2730
+ if (this.meta) {
2731
+ this.meta.active = false
2732
+ } else {
2733
+ this.active = false
2734
+ }
2735
+ return this
2012
2736
  }
2013
-
2014
- saveOne({ doc, systemLog }) {
2015
- const log = _makeLog({
2016
- systemLog,
2017
- label: 'REPO_WRITE',
2018
- message: `fn ${this._classname}.prototype.saveOne`,
2019
- input: [{ doc: { ...doc }, systemLog: { ...systemLog } }]
2020
- })
2021
- return new Promise((resolve, reject) => {
2022
- this.model.saveOne(doc, this.saveOptions, (err, result) => {
2023
- if (err) {
2024
- log({ level: 'warn', output: err.toString() })
2025
- reject(err)
2026
- } else {
2027
- log({ level: 'info', output: { ...result } })
2028
- resolve(result)
2029
- }
2030
- })
2031
- })
2737
+ unsetDeleted() {
2738
+ if (this.meta) {
2739
+ this.meta.deleted = false
2740
+ } else {
2741
+ this.deleted = false
2742
+ }
2743
+ return this
2032
2744
  }
2033
- }
2034
2745
 
2035
- function _makeLog({ systemLog, label, message: message1, input } = {}) {
2036
- return ({ level, messgae: massage2, output } = {}) => {
2037
- if (systemLog && systemLog.systemLogHelper) {
2038
- systemLog.systemLogHelper.log({
2039
- batchId: systemLog.batchId,
2040
- label,
2041
- level,
2042
- message: massage2 || message1,
2043
- data: {
2044
- payload: {
2045
- input,
2046
- output
2047
- }
2048
- }
2049
- })
2746
+ update(update) {
2747
+ if (update.meta) {
2748
+ this.meta = { ...this.meta, ...update.meta }
2050
2749
  }
2750
+ return this.setModified()
2051
2751
  }
2052
2752
  }
2053
2753
 
2754
+ ;// ./lib/models/trackedEntity/index.js
2054
2755
 
2756
+ // Explicit named export (optional)
2055
2757
 
2056
- ;// ./lib/models/repo/index.js
2758
+ ;// ./lib/models/tenantAwareEntity/tenantAwareEntity.js
2057
2759
 
2058
2760
 
2059
2761
 
2060
2762
 
2061
- ;// ./lib/models/service/service.js
2763
+ class TenantAwareEntity extends TrackedEntity {
2764
+ constructor(options = {}) {
2765
+ options = options || {}
2062
2766
 
2767
+ /**
2768
+ * instead of throw error, we choose to implement the isValid checking
2769
+ */
2770
+ // if (!options.tenantCode) {
2771
+ // throw new Error('tenantCode required')
2772
+ // }
2063
2773
 
2774
+ super(options)
2064
2775
 
2065
- class Service {
2066
- constructor({ repo }) {
2067
- this.repo = repo
2776
+ this._tenant = options._tenant
2777
+
2778
+ this.metadata = Metadata.initOnlyValidFromArray(options.metadata)
2779
+ this.remarks = KeyValueObject.initOnlyValidFromArray(options.remarks)
2780
+ this.tenantCode = options.tenantCode // Required for multi-tenancy
2068
2781
  }
2069
2782
 
2783
+ // Class methods
2070
2784
  static get _classname() {
2071
- return 'Service'
2785
+ return 'TenantAwareEntity'
2072
2786
  }
2073
2787
  static get _superclass() {
2074
- return 'Service'
2788
+ return 'TenantAwareEntity'
2075
2789
  }
2076
2790
 
2077
- deleteOne({ id }) {
2078
- return this.repo.deleteOne({ id })
2079
- .catch(() => {
2080
- throw new Error(`Not found for query: ${id}`)
2081
- })
2791
+ // getters
2792
+ get isValid() {
2793
+ return super.isValid && !!this.tenantCode // Required for multi-tenancy
2082
2794
  }
2083
2795
 
2084
- async findAll({ query = {}, systemLog } = {}) {
2085
- const result = await this.repo.findAll({ query, systemLog })
2086
- return makeApiResponse({
2087
- repo: this.repo,
2088
- result
2089
- })
2796
+ // instance methods
2797
+ getMetadata() {
2798
+ return this.metadata
2090
2799
  }
2091
-
2092
- async findOne({ query = {}, systemLog } = {}) {
2093
- const result = await this.repo.findOne({ query, systemLog })
2094
- return makeApiResponse({
2095
- repo: this.repo,
2096
- result
2097
- })
2800
+ getMetadataByKey(key) {
2801
+ return Metadata.foundByKey(this.metadata, key)
2098
2802
  }
2099
-
2100
- init(options) {
2101
- return this.repo.init(options)
2803
+ getMetadataValueByKey(key) {
2804
+ const found = this.getMetadataByKey(key)
2805
+ return found ? found.value : null
2102
2806
  }
2103
- initFromArray(arr = []) {
2104
- if (Array.isArray(arr)) {
2105
- return arr.map((a) => this.init(a))
2106
- }
2107
- return []
2807
+ getRemarks() {
2808
+ return this.remarks
2108
2809
  }
2109
- initOnlyValidFromArray(arr = []) {
2110
- return this.initFromArray(arr).filter((i) => i)
2810
+ getRemarkByKey(key) {
2811
+ return KeyValueObject.foundByKey(this.remarks, key)
2111
2812
  }
2112
-
2113
- async saveAll({ docs = [], systemLog } = {}) {
2114
- const copies = docs.map((doc) => {
2115
- return this.init(doc)
2116
- })
2117
- const result = await this.repo.saveAll({ docs: copies, systemLog })
2118
- return makeApiResponse({
2119
- repo: this.repo,
2120
- result
2121
- })
2813
+ getRemarksValueByKey(key) {
2814
+ const found = this.getRemarkByKey(key)
2815
+ return found ? found.value : null
2816
+ }
2817
+ getTenantCode() {
2818
+ return this.tenantCode
2122
2819
  }
2123
2820
 
2124
- async saveOne({ doc = {}, systemLog } = {}) {
2125
- const copy = this.init(doc)
2126
- if (copy) {
2127
- const result = await this.repo.saveOne({ doc: copy, systemLog })
2128
- return makeApiResponse({
2129
- repo: this.repo,
2130
- result
2131
- })
2821
+ update(update) {
2822
+ if (update.metadata && Array.isArray(update.metadata)) {
2823
+ this.metadata = Metadata.initOnlyValidFromArray(update.metadata)
2132
2824
  }
2133
- return {
2134
- isNew: null,
2135
- data: [],
2136
- err: new Error('doc is not a valid instance')
2825
+ if (update.remarks && Array.isArray(update.remarks)) {
2826
+ this.remarks = KeyValueObject.initOnlyValidFromArray(update.remarks)
2137
2827
  }
2828
+ return super.update(update)
2138
2829
  }
2139
2830
  }
2140
2831
 
2141
- function makeService({ repo }) {
2142
- if (repo === undefined) {
2143
- throw new Error('repo is required.')
2144
- }
2145
- if (repo._superclass !== Repo._superclass) {
2146
- throw new Error('repo is not an instance of Repo.')
2147
- }
2148
- return new Service({ repo })
2149
- }
2150
-
2151
-
2152
-
2153
- ;// ./lib/models/service/index.js
2154
-
2832
+ ;// ./lib/models/tenantAwareEntity/index.js
2155
2833
 
2156
2834
 
2157
2835
 
@@ -2213,191 +2891,6 @@ function _makeSetCode(fieldName, options) {
2213
2891
 
2214
2892
 
2215
2893
 
2216
- ;// ./lib/helpers/generalPost/generalPost.js
2217
-
2218
-
2219
-
2220
-
2221
- async function generalPost({ body = {}, GeneralModel, UniqueKeyGenerator, resourceInfo }) {
2222
- const { resources, data, globalShared = {}, shared = {}, relationship = {} } = body
2223
- const _resourceInfo = resourceInfo || body.resourceInfo
2224
- _attachShared(data, globalShared, shared)
2225
- const obj = await pReduce(resources, async (acc, resource) => {
2226
- const service = _makeService(resource, _resourceInfo, UniqueKeyGenerator, GeneralModel)
2227
- _createRelationship(data, relationship[resource], acc)
2228
- const _data = data[resource]
2229
- const result = await service.saveAll({ docs: [].concat(_data) })
2230
- acc[resource] = Array.isArray(_data) ? result._data : result._data[0]
2231
- return acc
2232
- }, {})
2233
- return obj
2234
- }
2235
-
2236
- function _attachShared(data, globalShared = {}, shared = {}) {
2237
- Object.keys(shared).forEach((key) => {
2238
- const _data = data[key]
2239
- data[key] = objectHelper.merge({}, _data, globalShared, shared[key] || {})
2240
- })
2241
- }
2242
-
2243
- function _createRelationship(data, relationship = {}, object) {
2244
- Object.keys(relationship).forEach((key) => {
2245
- const path = relationship[key]
2246
- const val = objectHelper.get(object, path)
2247
- objectHelper.set(data, key, val)
2248
- })
2249
- }
2250
-
2251
- function _makeService(resource, resourceInfo, UniqueKeyGenerator, GeneralModel) {
2252
- const { collectionName, fields } = resourceInfo[resource]
2253
- const uniqueKeyGenerator = UniqueKeyGenerator.makeGenerator(fields)
2254
- const model = new GeneralModel({ collectionName, uniqueKeyGenerator })
2255
- return makeService({
2256
- repo: new Repo({ model })
2257
- })
2258
- }
2259
-
2260
-
2261
-
2262
- ;// ./lib/helpers/generalPost/index.js
2263
-
2264
-
2265
-
2266
-
2267
- ;// ./lib/helpers/padZeros/padZeros.js
2268
- function padZeros(num, minLength = 6) {
2269
- num = num.toString()
2270
- if (num.length < minLength) {
2271
- return padZeros('0' + num, minLength)
2272
- }
2273
- return num
2274
- }
2275
-
2276
-
2277
-
2278
- ;// ./lib/helpers/padZeros/index.js
2279
-
2280
-
2281
-
2282
-
2283
- ;// ./lib/helpers/pReduce/index.js
2284
-
2285
-
2286
-
2287
-
2288
- ;// ./lib/helpers/stringFormatter/index.js
2289
-
2290
-
2291
-
2292
-
2293
- ;// ./lib/helpers/stringHelper/stringHelper.js
2294
- function baseXEncode(num, base = 34) {
2295
- const charset = getBaseCharset(base)
2296
- return encode(num, charset)
2297
- }
2298
-
2299
- function encode(int, charset) {
2300
- let byCode = charset.byCode;
2301
- if (int === 0) {
2302
- return byCode[0];
2303
- }
2304
-
2305
- var res = "",
2306
- max = charset.length;
2307
- while (int > 0) {
2308
- res = byCode[int % max] + res;
2309
- int = Math.floor(int / max);
2310
- }
2311
- return res;
2312
- }
2313
-
2314
- function getBaseCharset(base) {
2315
- let charset = '9876543210ABCDEFGHJKLMNPQRSTUVWXYZ'
2316
- if (base === 58) {
2317
- charset = '9876543210ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnpqrstuvwxyz'
2318
- }
2319
- return indexCharset(charset)
2320
- }
2321
-
2322
- function indexCharset(str) {
2323
- var byCode = {},
2324
- byChar = {},
2325
- length = str.length,
2326
- i, char;
2327
- for (i = 0; i < length; i++) {
2328
- char = str[i];
2329
- byCode[i] = char;
2330
- byChar[char] = i;
2331
- }
2332
- return { byCode: byCode, byChar: byChar, length: length };
2333
- }
2334
-
2335
- function randomString({ len = 16, pattern = 'a1' } = {}) {
2336
- const A = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'
2337
- const a = 'abcdefghijklmnopqrstuvwxyz'
2338
- const num = '1234567890'
2339
- const mark = '~!@#$%^&*_+-='
2340
- let str = ''
2341
- if (pattern.includes('A')) {
2342
- str += A
2343
- }
2344
- if (pattern.includes('a')) {
2345
- str += a
2346
- }
2347
- if (pattern.includes('1')) {
2348
- str += num
2349
- }
2350
- if (pattern.includes('#')) {
2351
- str += mark
2352
- }
2353
- const chars = [...str]
2354
- return [...Array(len)].map(i => {
2355
- return chars[(Math.random() * chars.length) | 0]
2356
- }).join``
2357
- }
2358
-
2359
- function reverse(str) {
2360
- if (typeof str !== 'string') {
2361
- str = str.toString()
2362
- }
2363
- const splitString = str.split('')
2364
- const reverseArray = splitString.reverse()
2365
- return reverseArray.join('')
2366
- }
2367
-
2368
- function setCode(base = 34) {
2369
- const now = (new Date()).valueOf()
2370
- const random = randomString({
2371
- len: 8,
2372
- pattern: '1'
2373
- })
2374
- const str = reverse(`${now}${random}`)
2375
- // const str = `${now}${random}`
2376
- return baseXEncode(str, base)
2377
- }
2378
-
2379
- const stringHelper = {
2380
- setCode
2381
- }
2382
-
2383
-
2384
- ;// ./lib/helpers/stringHelper/index.js
2385
-
2386
-
2387
-
2388
-
2389
-
2390
- ;// ./lib/helpers/index.js
2391
-
2392
-
2393
-
2394
-
2395
-
2396
-
2397
-
2398
-
2399
-
2400
-
2401
2894
 
2402
2895
 
2403
2896
  ;// ./lib/index.js