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