@questwork/q-utilities 0.1.12 → 0.1.13

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,6 +56,8 @@ __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),
60
62
  concatStringByArray: () => (/* reexport */ concatStringByArray),
61
63
  convertString: () => (/* reexport */ convertString),
@@ -63,6 +65,9 @@ __webpack_require__.d(__webpack_exports__, {
63
65
  generalPost: () => (/* reexport */ generalPost),
64
66
  getValidation: () => (/* reexport */ getValidation),
65
67
  getValueByKeys: () => (/* reexport */ getValueByKeys_getValueByKeys),
68
+ init: () => (/* reexport */ init),
69
+ initFromArray: () => (/* reexport */ initFromArray),
70
+ initOnlyValidFromArray: () => (/* reexport */ initOnlyValidFromArray),
66
71
  makeApiResponse: () => (/* reexport */ makeApiResponse),
67
72
  makeService: () => (/* reexport */ makeService),
68
73
  objectHelper: () => (/* reexport */ objectHelper),
@@ -289,7 +294,6 @@ function getValueByKeys_getValueByKeys(keys, data) {
289
294
  });
290
295
 
291
296
 
292
-
293
297
  ;// ./lib/helpers/getValueByKeys/index.js
294
298
 
295
299
 
@@ -317,7 +321,25 @@ class TemplateCompilerException extends Error {
317
321
  ;// ./lib/models/templateCompiler/constants.js
318
322
  const _EMPTY = '_EMPTY'
319
323
  const _FN_NAMES = [
320
- 'get', 'map', 'join', 'concatIf', 'exec', 'filterOne', 'filterAll', 'formatDate', 'eq', 'neq', 'gt', 'lt', 'gte', 'lte', 'isEmpty', 'isNotEmpty', 'toLowerCase', 'toUpperCase'
324
+ 'concatIf',
325
+ 'divide',
326
+ 'eq',
327
+ 'exec',
328
+ 'filterAll',
329
+ 'filterOne',
330
+ 'formatDate',
331
+ 'get',
332
+ 'gt',
333
+ 'gte',
334
+ 'isEmpty',
335
+ 'isNotEmpty',
336
+ 'join',
337
+ 'lt',
338
+ 'lte',
339
+ 'map',
340
+ 'neq',
341
+ 'toLowerCase',
342
+ 'toUpperCase',
321
343
  ]
322
344
  const _HIDE = '_HIDE'
323
345
  const _NOT_EMPTY = '_NOT_EMPTY'
@@ -358,6 +380,20 @@ function _concatIf(data, args) {
358
380
 
359
381
 
360
382
 
383
+ ;// ./lib/models/templateCompiler/helpers/_divide.js
384
+ function _divide(value, divisor) {
385
+ try {
386
+ if (Number.isNaN(value)) {
387
+ return value
388
+ }
389
+ return (value / divisor)
390
+ } catch (e) {
391
+ throw e
392
+ }
393
+ }
394
+
395
+
396
+
361
397
  ;// ./lib/models/templateCompiler/helpers/_eq.js
362
398
 
363
399
 
@@ -943,6 +979,7 @@ function _toUpperCase(data, args) {
943
979
 
944
980
 
945
981
 
982
+
946
983
 
947
984
 
948
985
  ;// ./lib/models/templateCompiler/templateCompiler.js
@@ -969,6 +1006,9 @@ class TemplateCompiler {
969
1006
  static concatIf(data, args) {
970
1007
  return _concatIf(data, args)
971
1008
  }
1009
+ static divide(data, args) {
1010
+ return _divide(data, args)
1011
+ }
972
1012
  static eq(data, args) {
973
1013
  return _eq(data, args)
974
1014
  }
@@ -980,7 +1020,6 @@ class TemplateCompiler {
980
1020
  static formatDate(data, args) {
981
1021
  return _formatDate(data, args)
982
1022
  }
983
-
984
1023
  static get(data, key, failover = null) {
985
1024
  return _get(data, key, failover)
986
1025
  }
@@ -1102,6 +1141,10 @@ function _parseSinglePart(input) {
1102
1141
  // 去掉双引号,返回
1103
1142
  return input.substring(1, input.length - 1)
1104
1143
  }
1144
+ if (input.startsWith("'") && input.endsWith("'")) {
1145
+ // 去掉双引号,返回
1146
+ return input.substring(1, input.length - 1)
1147
+ }
1105
1148
 
1106
1149
  const _input = _toBasicType(input)
1107
1150
 
@@ -1129,6 +1172,10 @@ function _toBasicType(input) {
1129
1172
  // 去掉双引号,返回
1130
1173
  return input.substring(1, input.length - 1)
1131
1174
  }
1175
+ if (input.startsWith("'") && input.endsWith("'")) {
1176
+ // 去掉双引号,返回
1177
+ return input.substring(1, input.length - 1)
1178
+ }
1132
1179
  if (input === 'true') {
1133
1180
  return true
1134
1181
  }
@@ -1151,8 +1198,20 @@ function _callFunction(data, functionName, parameters) {
1151
1198
  try {
1152
1199
  let failover
1153
1200
  switch (functionName) {
1201
+ case 'concatIf':
1202
+ return _concatIf(data, parameters)
1203
+ case 'divide':
1204
+ return _divide(data, parameters)
1205
+ case 'eq':
1206
+ return _eq(data, parameters)
1154
1207
  case 'exec':
1155
1208
  return _exec(data, parameters)
1209
+ case 'filterAll':
1210
+ return _filterAll(data, parameters)
1211
+ case 'filterOne':
1212
+ return _filterOne(data, parameters)
1213
+ case 'formatDate':
1214
+ return _formatDate(data, parameters)
1156
1215
  case 'get':
1157
1216
  if (parameters.length > 2) {
1158
1217
  throw new TemplateCompilerException(TEMPLATE_COMPILER_EXCEPTION_TYPE.argumentFormatException)
@@ -1161,34 +1220,24 @@ function _callFunction(data, functionName, parameters) {
1161
1220
  failover = parameters[parameters.length - 1]
1162
1221
  }
1163
1222
  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
1223
  case 'gt':
1181
1224
  return _gt(data, parameters)
1182
1225
  case 'gte':
1183
1226
  return _gte(data, parameters)
1184
- case 'lt':
1185
- return _lt(data, parameters)
1186
- case 'lte':
1187
- return _lte(data, parameters)
1188
1227
  case 'isEmpty':
1189
1228
  return _isEmpty(data, parameters)
1190
1229
  case 'isNotEmpty':
1191
1230
  return _isNotEmpty(data, parameters)
1231
+ case 'join':
1232
+ return _join(data, parameters[0])
1233
+ case 'lt':
1234
+ return _lt(data, parameters)
1235
+ case 'lte':
1236
+ return _lte(data, parameters)
1237
+ case 'map':
1238
+ return _map(data, parameters)
1239
+ case 'neq':
1240
+ return _neq(data, parameters)
1192
1241
  case 'toLowerCase':
1193
1242
  return _toLowerCase(data)
1194
1243
  case 'toUpperCase':
@@ -1440,282 +1489,468 @@ async function pReduce(iterable, reducer, initialValue) {
1440
1489
 
1441
1490
 
1442
1491
 
1443
- ;// ./lib/models/apiResponse/apiResponse.js
1444
- class ApiResponse {
1445
- constructor(options = {}) {
1492
+ ;// ./lib/models/repo/repo.js
1493
+ class Repo {
1494
+ constructor(options) {
1446
1495
  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
1496
+ this.model = options.model
1497
+ this._sharedOptions = options._sharedOptions // { session: this.dbTransaction }
1498
+ this._queryOptions = options._queryOptions
1499
+ this._saveOptions = options._saveOptions
1500
+ this._Class = options._constructor && options._constructor._Class
1501
+ ? options._constructor._Class
1502
+ : null
1453
1503
  }
1454
-
1455
1504
  static init(options = {}) {
1456
1505
  if (options instanceof this) {
1457
1506
  return options
1458
1507
  }
1459
1508
  const instance = new this(options)
1460
- return instance
1509
+ return instance.isValid ? instance : null
1461
1510
  }
1462
1511
  static get _classname() {
1463
- return 'ApiResponse'
1512
+ return 'Repo'
1464
1513
  }
1465
1514
  static get _superclass() {
1466
- return 'ApiResponse'
1515
+ return 'Repo'
1467
1516
  }
1468
1517
 
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
1518
+ get _classname() {
1519
+ return 'Repo'
1475
1520
  }
1476
- }
1477
-
1478
-
1479
1521
 
1480
- ;// ./lib/models/apiResponse/makeApiResponse.js
1522
+ get _superclass() {
1523
+ return 'Repo'
1524
+ }
1481
1525
 
1526
+ get isValid() {
1527
+ return this.model
1528
+ && (typeof this.model.deleteOne === 'function')
1529
+ && (typeof this.model.findAll === 'function')
1530
+ && (typeof this.model.saveOne === 'function')
1531
+ }
1482
1532
 
1483
- function makeApiResponse({ repo, result }) {
1484
- return ApiResponse.init({
1485
- ...result,
1486
- _instanceBuilder: (i) => {
1487
- return repo.init(i)
1533
+ get queryOptions() {
1534
+ return {
1535
+ ...this._sharedOptions,
1536
+ ...this._queryOptions,
1488
1537
  }
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
1538
  }
1507
1539
 
1508
- // Class methods
1509
- static init(options = {}) {
1510
- if (options instanceof this) {
1511
- return options
1540
+ get saveOptions() {
1541
+ return {
1542
+ ...this._sharedOptions,
1543
+ ...this._saveOptions,
1512
1544
  }
1513
- const instance = new this(options)
1514
- return instance.isValid ? instance : null
1515
1545
  }
1516
- static initFromArray(arr = []) {
1517
- if (Array.isArray(arr)) {
1518
- return arr.map((a) => this.init(a))
1546
+
1547
+ init(options) {
1548
+ if (this._Class && typeof this._Class.init === 'function') {
1549
+ return this._Class.init(options)
1519
1550
  }
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'
1551
+ return options
1530
1552
  }
1531
1553
 
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 }))
1554
+ async deleteOne({ id }) {
1555
+ try {
1556
+ const result = await this.model.deleteOne({ _id: id })
1557
+ return {
1558
+ ...result, // { message: 'ok', total }
1559
+ isNew: false,
1560
+ data: []
1561
+ }
1562
+ } catch (err) {
1563
+ throw err
1538
1564
  }
1539
- return arr
1540
1565
  }
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
1566
+
1567
+ // systemLog is optional
1568
+ findAll({ query, systemLog }) {
1569
+ const log = _makeLog({
1570
+ systemLog,
1571
+ label: 'REPO_READ',
1572
+ message: `fn ${this._classname}.prototype.findAll`,
1573
+ input: [{ query: { ...query }, systemLog: { ...systemLog } }]
1547
1574
  })
1548
- }
1549
- static appendValueArray(arr = [], key, value) {
1550
- return arr.map((item) => {
1551
- if (this.sameKey(item, key)) {
1552
- item.value = [...item.value, ...value]
1553
- }
1554
- return item
1575
+ return new Promise((resolve, reject) => {
1576
+ this.model.findAll(query, this.queryOptions, (err, data, total) => {
1577
+ if (err) {
1578
+ log({ level: 'warn', output: err.toString() })
1579
+ reject(err)
1580
+ } else {
1581
+ const result = {
1582
+ isNew: false,
1583
+ data,
1584
+ total: total || data.length
1585
+ }
1586
+ log({ level: 'info', output: { ...result } })
1587
+ resolve(result)
1588
+ }
1589
+ })
1555
1590
  })
1556
1591
  }
1557
- static foundByKey(arr = [], key) {
1558
- const found = arr.find((m) => {
1559
- return this.sameKey(m, key)
1592
+
1593
+ findOne({ query, systemLog }) {
1594
+ const log = _makeLog({
1595
+ systemLog,
1596
+ label: 'REPO_READ',
1597
+ message: `fn ${this._classname}.prototype.findOne`,
1598
+ input: [{ query: { ...query }, systemLog: { ...systemLog } }]
1560
1599
  })
1561
- return found || null
1562
- }
1563
- static foundValueByKey(arr = [], key) {
1564
- const found = this.foundByKey(arr, key)
1565
- return found ? found.value : null
1600
+ return new Promise((resolve, reject) => {
1601
+ this.model.findAll(query, this.queryOptions, (err, data) => {
1602
+ if (err) {
1603
+ reject(err)
1604
+ } else if (data.length === 1) {
1605
+ const result = {
1606
+ isNew: false,
1607
+ data,
1608
+ total: 1
1609
+ }
1610
+ log({ level: 'info', output: { ...result } })
1611
+ resolve(result)
1612
+ } else if (data.length === 0) {
1613
+ reject(new Error('record not found'))
1614
+ } else {
1615
+ reject(new Error('more than one is found'))
1616
+ }
1617
+ })
1618
+ })
1619
+ .catch((err) => {
1620
+ log({ level: 'warn', output: err.toString() })
1621
+ throw err
1622
+ })
1566
1623
  }
1567
- static fromObject(options = {}) {
1568
- return Object.keys(options).reduce((acc, key) => {
1569
- acc.push(this.init({ key, value: options[key] }))
1570
- return acc
1571
- }, [])
1572
- }
1573
- static getValueByKey(arr = [], key) {
1574
- return this.foundValueByKey(arr, key)
1624
+
1625
+ saveAll({ docs, systemLog }) {
1626
+ let isNew
1627
+ const log = _makeLog({
1628
+ systemLog,
1629
+ label: 'REPO_WRITE',
1630
+ message: `fn ${this._classname}.prototype.saveAll`,
1631
+ input: [{ docs: [...docs], systemLog: { ...systemLog } }]
1632
+ })
1633
+ const promise = typeof this.model.saveAll === 'function'
1634
+ ? this.model.saveAll({ docs })
1635
+ : Promise.all(docs.map(async (doc) => {
1636
+ if (doc) {
1637
+ const result = await this.saveOne({ doc })
1638
+ isNew = result.isNew
1639
+ const _data = result._data || result.data
1640
+ return _data[0]
1641
+ }
1642
+ return null
1643
+ }))
1644
+ return promise.then((savedData) => {
1645
+ if (savedData.length !== 1) isNew = null
1646
+ const result = {
1647
+ data: savedData,
1648
+ isNew,
1649
+ total: savedData.length
1650
+ }
1651
+ log({ level: 'info', output: { ...result } })
1652
+ return result
1653
+ }).catch((err) => {
1654
+ log({ level: 'warn', output: err.toString() })
1655
+ throw err
1656
+ })
1575
1657
  }
1576
- static getValueByKeyFromArray(arr = [], key) {
1577
- if (arr.length === 0) {
1578
- return null
1579
- }
1580
- const firstArr = arr.shift()
1581
- const found = firstArr.find((i) => {
1582
- return this.sameKey(i, key)
1658
+
1659
+ saveOne({ doc, systemLog }) {
1660
+ const log = _makeLog({
1661
+ systemLog,
1662
+ label: 'REPO_WRITE',
1663
+ message: `fn ${this._classname}.prototype.saveOne`,
1664
+ input: [{ doc: { ...doc }, systemLog: { ...systemLog } }]
1583
1665
  })
1584
- if (found && found.value) {
1585
- return found.value
1666
+ return new Promise((resolve, reject) => {
1667
+ this.model.saveOne(doc, this.saveOptions, (err, result) => {
1668
+ if (err) {
1669
+ log({ level: 'warn', output: err.toString() })
1670
+ reject(err)
1671
+ } else {
1672
+ log({ level: 'info', output: { ...result } })
1673
+ resolve(result)
1674
+ }
1675
+ })
1676
+ })
1677
+ }
1678
+ }
1679
+
1680
+ function _makeLog({ systemLog, label, message: message1, input } = {}) {
1681
+ return ({ level, messgae: massage2, output } = {}) => {
1682
+ if (systemLog && systemLog.systemLogHelper) {
1683
+ systemLog.systemLogHelper.log({
1684
+ batchId: systemLog.batchId,
1685
+ label,
1686
+ level,
1687
+ message: massage2 || message1,
1688
+ data: {
1689
+ payload: {
1690
+ input,
1691
+ output
1692
+ }
1693
+ }
1694
+ })
1586
1695
  }
1587
- return this.getValueByKeyFromArray(arr, key)
1588
1696
  }
1589
- static getValuesByKey(arr = [], key) {
1590
- return arr.reduce((acc, item) => {
1591
- if (this.sameKey(item, key)) {
1592
- acc.push(item.value)
1593
- }
1594
- return acc
1595
- }, [])
1697
+ }
1698
+
1699
+
1700
+
1701
+ ;// ./lib/models/apiResponse/apiResponse.js
1702
+ class ApiResponse {
1703
+ constructor(options = {}) {
1704
+ options = options || {}
1705
+ this._data = options.data || options._data || []
1706
+ this.err = options.err
1707
+ this.isNew = options.isNew || false
1708
+ this.message = options.message
1709
+ this.total = options.total || 0
1710
+ this._instanceBuilder = options._instanceBuilder
1596
1711
  }
1597
- static hasKeyValue(arr = [], key, value) {
1598
- if (typeof value === 'undefined') {
1599
- return arr.filter((item) => this.sameKey(item, key)).length > 0
1712
+
1713
+ static init(options = {}) {
1714
+ if (options instanceof this) {
1715
+ return options
1600
1716
  }
1601
- return arr.filter((item) => (this.sameKey(item, key) && _isSame(item.value, value))).length > 0
1717
+ const instance = new this(options)
1718
+ return instance
1602
1719
  }
1603
- static insertOrUpdateRecord(arr = [], key, value) {
1604
- let copy = [...arr]
1605
- if (!this.hasKeyValue(arr, key)) {
1606
- copy.push(this.init({ key, value }))
1607
- } else {
1608
- copy = this.updateRecord(arr, key, value)
1720
+ static get _classname() {
1721
+ return 'ApiResponse'
1722
+ }
1723
+ static get _superclass() {
1724
+ return 'ApiResponse'
1725
+ }
1726
+
1727
+ // getters
1728
+ get data() {
1729
+ if (this._instanceBuilder && (typeof this._instanceBuilder === 'function')) {
1730
+ return this._data.map(this._instanceBuilder)
1609
1731
  }
1610
- return copy
1732
+ return this._data
1611
1733
  }
1612
- static keys(arr = []) {
1613
- if (Array.isArray(arr)) {
1614
- return arr.reduce((acc, item) => {
1615
- acc.push(item.key)
1616
- return acc
1617
- }, [])
1734
+ }
1735
+
1736
+
1737
+
1738
+ ;// ./lib/models/apiResponse/makeApiResponse.js
1739
+
1740
+
1741
+ function makeApiResponse({ repo, result }) {
1742
+ return ApiResponse.init({
1743
+ ...result,
1744
+ _instanceBuilder: (i) => {
1745
+ return repo.init(i)
1618
1746
  }
1619
- return []
1747
+ })
1748
+ }
1749
+
1750
+
1751
+
1752
+ ;// ./lib/models/service/service.js
1753
+
1754
+
1755
+
1756
+ class Service {
1757
+ constructor({ repo }) {
1758
+ this.repo = repo
1620
1759
  }
1621
- static merge(toArr, fromArr) {
1622
- (fromArr || []).map((from) => {
1623
- const found = toArr.find((to) => {
1624
- return to.key === from.key
1760
+
1761
+ static get _classname() {
1762
+ return 'Service'
1763
+ }
1764
+ static get _superclass() {
1765
+ return 'Service'
1766
+ }
1767
+
1768
+ deleteOne({ id }) {
1769
+ return this.repo.deleteOne({ id })
1770
+ .catch(() => {
1771
+ throw new Error(`Not found for query: ${id}`)
1625
1772
  })
1626
- if (found) {
1627
- found.value = (found.value || []).concat(from.value)
1628
- } else {
1629
- toArr.push(from)
1630
- }
1773
+ }
1774
+
1775
+ async findAll({ query = {}, systemLog } = {}) {
1776
+ const result = await this.repo.findAll({ query, systemLog })
1777
+ return makeApiResponse({
1778
+ repo: this.repo,
1779
+ result
1631
1780
  })
1632
- return toArr
1633
1781
  }
1634
- static removeByKey(arr, key) {
1635
- return arr.reduce((acc, item) => {
1636
- if (!this.sameKey(item, key)) {
1637
- acc.push(item)
1638
- }
1639
- return acc
1640
- }, [])
1782
+
1783
+ async findOne({ query = {}, systemLog } = {}) {
1784
+ const result = await this.repo.findOne({ query, systemLog })
1785
+ return makeApiResponse({
1786
+ repo: this.repo,
1787
+ result
1788
+ })
1641
1789
  }
1642
- static sameKey(item, key) {
1643
- return _isSame(item.key, key)
1790
+
1791
+ init(options) {
1792
+ return this.repo.init(options)
1644
1793
  }
1645
- static toObject(arr = []) {
1794
+ initFromArray(arr = []) {
1646
1795
  if (Array.isArray(arr)) {
1647
- return arr.reduce((acc, item) => {
1648
- acc[item.key] = item.value
1649
- return acc
1650
- }, {})
1796
+ return arr.map((a) => this.init(a))
1651
1797
  }
1652
- return {}
1798
+ return []
1653
1799
  }
1654
- static toString(arr = [], delimiter = '; ') {
1655
- if (Array.isArray(arr)) {
1656
- return arr.reduce((acc, item) => {
1657
- acc.push(`${item.key}: ${item.value}`)
1658
- return acc
1659
- }, []).join(delimiter)
1660
- }
1661
- return ''
1800
+ initOnlyValidFromArray(arr = []) {
1801
+ return this.initFromArray(arr).filter((i) => i)
1662
1802
  }
1663
- static updateRecord(arr = [], key, value) {
1664
- return arr.map((item) => {
1665
- if (this.sameKey(item, key)) {
1666
- return {
1667
- ...item,
1668
- value
1669
- }
1670
- }
1671
- return item
1803
+
1804
+ async saveAll({ docs = [], config = {}, systemLog } = {}) {
1805
+ const copies = docs.map((doc) => {
1806
+ return config.skipInit ? doc : this.init(doc)
1807
+ })
1808
+ const result = await this.repo.saveAll({ docs: copies, systemLog })
1809
+ return makeApiResponse({
1810
+ repo: this.repo,
1811
+ result
1672
1812
  })
1673
1813
  }
1674
- static updateOrInsertRecord(arr = [], key, value) {
1675
- return this.insertOrUpdateRecord(arr, key, value)
1676
- }
1677
- static updateRecordsFromArray(arr = [], updateArr = []) {
1678
- if (Array.isArray(arr) && Array.isArray(updateArr)) {
1679
- const obj1 = this.toObject(arr)
1680
- const obj2 = this.toObject(updateArr)
1681
- return this.fromObject({
1682
- ...obj1,
1683
- ...obj2
1814
+
1815
+ // set skipInit to true if we want to use POST for query
1816
+ async saveOne({ doc = {}, config = {}, systemLog } = {}) {
1817
+ const copy = config.skipInit ? doc : this.init(doc)
1818
+ if (copy) {
1819
+ const result = await this.repo.saveOne({ doc: copy, systemLog })
1820
+ return makeApiResponse({
1821
+ repo: this.repo,
1822
+ result
1684
1823
  })
1685
1824
  }
1686
- return []
1687
- }
1688
- static values(arr = []) {
1689
- if (Array.isArray(arr)) {
1690
- return arr.reduce((acc, item) => {
1691
- acc.push(item.value)
1692
- return acc
1693
- }, [])
1825
+ return {
1826
+ isNew: null,
1827
+ data: [],
1828
+ err: new Error('doc is not a valid instance')
1694
1829
  }
1695
- return []
1696
1830
  }
1831
+ }
1697
1832
 
1698
- // getters
1699
- get isValid() {
1700
- return !!this.key
1833
+ function makeService({ repo }) {
1834
+ if (repo === undefined) {
1835
+ throw new Error('repo is required.')
1701
1836
  }
1837
+ if (repo._superclass !== Repo._superclass) {
1838
+ throw new Error('repo is not an instance of Repo.')
1839
+ }
1840
+ return new Service({ repo })
1841
+ }
1702
1842
 
1703
- get toObject() {
1704
- const obj = {}
1705
- if (this.isValid) {
1706
- obj[this.key] = this.value
1707
- }
1708
- return obj
1843
+
1844
+
1845
+ ;// ./lib/helpers/generalPost/generalPost.js
1846
+
1847
+
1848
+
1849
+
1850
+
1851
+ async function generalPost({ body = {}, GeneralModel, UniqueKeyGenerator, resourceInfo }) {
1852
+ const { resources, data, globalShared = {}, shared = {}, relationship = {} } = body
1853
+ const _resourceInfo = resourceInfo || body.resourceInfo
1854
+ _attachShared(data, globalShared, shared)
1855
+ const obj = await pReduce(resources, async (acc, resource) => {
1856
+ const service = _makeService(resource, _resourceInfo, UniqueKeyGenerator, GeneralModel)
1857
+ _createRelationship(data, relationship[resource], acc)
1858
+ const _data = data[resource]
1859
+ const result = await service.saveAll({ docs: [].concat(_data) })
1860
+ acc[resource] = Array.isArray(_data) ? result._data : result._data[0]
1861
+ return acc
1862
+ }, {})
1863
+ return obj
1864
+ }
1865
+
1866
+ function _attachShared(data, globalShared = {}, shared = {}) {
1867
+ Object.keys(shared).forEach((key) => {
1868
+ const _data = data[key]
1869
+ data[key] = objectHelper.merge({}, _data, globalShared, shared[key] || {})
1870
+ })
1871
+ }
1872
+
1873
+ function _createRelationship(data, relationship = {}, object) {
1874
+ Object.keys(relationship).forEach((key) => {
1875
+ const path = relationship[key]
1876
+ const val = objectHelper.get(object, path)
1877
+ objectHelper.set(data, key, val)
1878
+ })
1879
+ }
1880
+
1881
+ function _makeService(resource, resourceInfo, UniqueKeyGenerator, GeneralModel) {
1882
+ const { collectionName, fields } = resourceInfo[resource]
1883
+ const uniqueKeyGenerator = UniqueKeyGenerator.makeGenerator(fields)
1884
+ const model = new GeneralModel({ collectionName, uniqueKeyGenerator })
1885
+ return makeService({
1886
+ repo: new Repo({ model })
1887
+ })
1888
+ }
1889
+
1890
+
1891
+
1892
+ ;// ./lib/helpers/generalPost/index.js
1893
+
1894
+
1895
+
1896
+
1897
+ ;// ./lib/helpers/init/init.js
1898
+ function init(_class, options) {
1899
+ if (options instanceof _class) {
1900
+ return options
1901
+ }
1902
+ try {
1903
+ const instance = new _class(options)
1904
+ return instance.isValid ? instance : null
1905
+ } catch (e) {
1906
+ console.log(`init failed for class: ${_class._classname || 'no _classname'}`, e)
1907
+ return null
1709
1908
  }
1710
1909
  }
1711
1910
 
1712
- function _isSame(key1, key2) {
1713
- return key1 === key2
1911
+ ;// ./lib/helpers/init/index.js
1912
+
1913
+
1914
+ ;// ./lib/helpers/initFromArray/initFromArray.js
1915
+
1916
+
1917
+ function initFromArray(_class, arr) {
1918
+ if (Array.isArray(arr)) {
1919
+ return arr.map((a) => init(_class, a))
1920
+ }
1921
+ return []
1714
1922
  }
1715
1923
 
1924
+ ;// ./lib/helpers/initFromArray/index.js
1716
1925
 
1717
1926
 
1718
- ;// ./lib/models/keyValueObject/index.js
1927
+ ;// ./lib/helpers/initOnlyValidFromArray/initOnlyValidFromArray.js
1928
+
1929
+
1930
+ function initOnlyValidFromArray(_class, arr) {
1931
+ return initFromArray(_class, arr).filter((i) => i)
1932
+ }
1933
+
1934
+ ;// ./lib/helpers/initOnlyValidFromArray/index.js
1935
+
1936
+
1937
+ ;// ./lib/helpers/padZeros/padZeros.js
1938
+ function padZeros(num, minLength = 6) {
1939
+ num = num.toString()
1940
+ if (num.length < minLength) {
1941
+ return padZeros('0' + num, minLength)
1942
+ }
1943
+ return num
1944
+ }
1945
+
1946
+
1947
+
1948
+ ;// ./lib/helpers/padZeros/index.js
1949
+
1950
+
1951
+
1952
+
1953
+ ;// ./lib/helpers/pReduce/index.js
1719
1954
 
1720
1955
 
1721
1956
 
@@ -1734,129 +1969,161 @@ function stringFormatter(str, delimiter = '_') {
1734
1969
 
1735
1970
 
1736
1971
 
1737
- ;// ./lib/models/metadata/metadata.js
1972
+ ;// ./lib/helpers/stringFormatter/index.js
1738
1973
 
1739
1974
 
1740
1975
 
1741
- const DELIMITER = '_'
1742
1976
 
1743
- class Metadata extends KeyValueObject {
1744
- static init(options = {}) {
1745
- if (options instanceof this) {
1746
- return options
1747
- }
1748
- const instance = new this({
1749
- ...options,
1750
- key: stringFormatter(options.key, DELIMITER),
1751
- })
1752
- return instance.isValid ? instance : null
1977
+ ;// ./lib/helpers/stringHelper/stringHelper.js
1978
+ function baseXEncode(num, base = 34) {
1979
+ const charset = getBaseCharset(base)
1980
+ return encode(num, charset)
1981
+ }
1982
+
1983
+ function encode(int, charset) {
1984
+ const { byCode } = charset
1985
+ if (int === 0) {
1986
+ return byCode[0]
1753
1987
  }
1754
- static get _classname() {
1755
- return 'Metadata'
1988
+
1989
+ let res = ''
1990
+ const max = charset.length
1991
+ while (int > 0) {
1992
+ res = byCode[int % max] + res
1993
+ int = Math.floor(int / max)
1756
1994
  }
1995
+ return res
1996
+ }
1757
1997
 
1758
- static merge(toArr, fromArr) {
1759
- (fromArr || []).map((from) => {
1760
- const found = toArr.find((to) => {
1761
- return metadata_isSame(to.key, from.key)
1762
- })
1763
- if (found) {
1764
- found.value = (found.value || []).concat(from.value)
1765
- } else {
1766
- toArr.push(from)
1998
+ function getBaseCharset(base) {
1999
+ let charset = '9876543210ABCDEFGHJKLMNPQRSTUVWXYZ'
2000
+ if (base === 58) {
2001
+ charset = '9876543210ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnpqrstuvwxyz'
2002
+ }
2003
+ return indexCharset(charset)
2004
+ }
2005
+
2006
+ function indexCharset(str) {
2007
+ const byCode = {}
2008
+ const byChar = {}
2009
+ const { length } = str
2010
+ let char
2011
+ for (let i = 0; i < length; i++) {
2012
+ char = str[i]
2013
+ byCode[i] = char
2014
+ byChar[char] = i;
2015
+ }
2016
+ return { byCode, byChar, length }
2017
+ }
2018
+
2019
+ function isSame(str1, str2) {
2020
+ if (typeof str1 !== 'string' || typeof str2 !== 'string') {
2021
+ return false
2022
+ }
2023
+ return str1.trim().toUpperCase() === str2.trim().toUpperCase()
2024
+ }
2025
+
2026
+ function randomString({ len = 16, pattern = 'a1' } = {}) {
2027
+ const A = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'
2028
+ const a = 'abcdefghijklmnopqrstuvwxyz'
2029
+ const num = '1234567890'
2030
+ const mark = '~!@#$%^&*_+-='
2031
+ let str = ''
2032
+ if (pattern.includes('A')) {
2033
+ str += A
2034
+ }
2035
+ if (pattern.includes('a')) {
2036
+ str += a
2037
+ }
2038
+ if (pattern.includes('1')) {
2039
+ str += num
2040
+ }
2041
+ if (pattern.includes('#')) {
2042
+ str += mark
2043
+ }
2044
+ const chars = [...str]
2045
+ return [...Array(len)].map(i => {
2046
+ return chars[(Math.random() * chars.length) | 0]
2047
+ }).join``
2048
+ }
2049
+
2050
+ function reverse(str) {
2051
+ const _str = (typeof str !== 'string') ? str.toString() : str
2052
+ const splitString = _str.split('')
2053
+ const reverseArray = splitString.reverse()
2054
+ return reverseArray.join('')
2055
+ }
2056
+
2057
+ function setCode(base = 34) {
2058
+ const now = (new Date()).valueOf()
2059
+ const random = randomString({
2060
+ len: 8,
2061
+ pattern: '1'
2062
+ })
2063
+ const str = reverse(`${now}${random}`)
2064
+ // const str = `${now}${random}`
2065
+ return baseXEncode(str, base)
2066
+ }
2067
+
2068
+ function toCamelCase(str) {
2069
+ if (!str) return ''
2070
+ return str
2071
+ .trim()
2072
+ .split(/\s+/)
2073
+ .map((word, index) => {
2074
+ if (!word) return ''
2075
+ if (index === 0) {
2076
+ return word.toLowerCase()
1767
2077
  }
2078
+ return word.charAt(0).toUpperCase() + word.slice(1).toLowerCase()
1768
2079
  })
1769
- return toArr
1770
- }
1771
- static sameKey(item, key) {
1772
- return metadata_isSame(item.key, key)
1773
- }
2080
+ .join('')
1774
2081
  }
1775
2082
 
1776
- function metadata_isSame(key1, key2) {
1777
- return stringFormatter(key1, DELIMITER) === stringFormatter(key2, DELIMITER)
2083
+ const stringHelper = {
2084
+ isSame,
2085
+ setCode,
2086
+ toCamelCase,
1778
2087
  }
1779
2088
 
1780
2089
 
1781
2090
 
1782
- ;// ./lib/models/metadata/index.js
2091
+ ;// ./lib/helpers/stringHelper/index.js
1783
2092
 
1784
2093
 
1785
2094
 
1786
2095
 
1787
- ;// ./lib/models/qMeta/qMeta.js
2096
+ ;// ./lib/helpers/index.js
1788
2097
 
1789
2098
 
1790
- const updateAllowedProps = [
1791
- 'attributes',
1792
- 'ref'
1793
- ]
1794
2099
 
1795
- class QMeta {
1796
- constructor(options = {}) {
1797
- options = options || {}
1798
- this.attributes = KeyValueObject.initOnlyValidFromArray(options.attributes)
1799
- this.ref = options.ref || {}
1800
- }
1801
2100
 
1802
- static get _classname() {
1803
- return 'QMeta'
1804
- }
1805
- static get _superclass() {
1806
- return 'QMeta'
1807
- }
1808
2101
 
1809
- // Class methods
1810
- static init(options = {}) {
1811
- if (options instanceof QMeta) {
1812
- return options
1813
- }
1814
- return new QMeta(options)
1815
- }
1816
2102
 
1817
- // instance methods
1818
- addAttribute(obj) {
1819
- const kvObject = KeyValueObject.init(obj)
1820
- if (!kvObject) {
1821
- throw new Error('invalid meta attribute')
1822
- }
1823
- this.attributes.push(kvObject)
1824
- return this
1825
- }
1826
2103
 
1827
- update(obj) {
1828
- Object.keys(obj).forEach((key) => {
1829
- if (updateAllowedProps.includes(key)) {
1830
- if (key === 'attributes') {
1831
- this[key] = KeyValueObject.initOnlyValidFromArray(obj[key])
1832
- } else {
1833
- this[key] = obj[key]
1834
- }
1835
- }
1836
- })
1837
- return this
1838
- }
1839
- }
1840
2104
 
1841
2105
 
1842
2106
 
1843
- ;// ./lib/models/qMeta/index.js
1844
2107
 
1845
2108
 
1846
2109
 
1847
2110
 
1848
- ;// ./lib/models/repo/repo.js
1849
- class Repo {
1850
- constructor(options) {
2111
+
2112
+ ;// ./lib/models/apiResponse/index.js
2113
+
2114
+
2115
+
2116
+
2117
+
2118
+ ;// ./lib/models/keyValueObject/keyValueObject.js
2119
+ class KeyValueObject {
2120
+ constructor(options = {}) {
1851
2121
  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
2122
+ this.key = options.key || null
2123
+ this.value = (typeof options.value !== 'undefined') ? options.value : ''
1859
2124
  }
2125
+
2126
+ // Class methods
1860
2127
  static init(options = {}) {
1861
2128
  if (options instanceof this) {
1862
2129
  return options
@@ -1864,532 +2131,484 @@ class Repo {
1864
2131
  const instance = new this(options)
1865
2132
  return instance.isValid ? instance : null
1866
2133
  }
2134
+ static initFromArray(arr = []) {
2135
+ if (Array.isArray(arr)) {
2136
+ return arr.map((a) => this.init(a))
2137
+ }
2138
+ return []
2139
+ }
2140
+ static initOnlyValidFromArray(arr = []) {
2141
+ return this.initFromArray(arr).filter((i) => i)
2142
+ }
1867
2143
  static get _classname() {
1868
- return 'Repo'
2144
+ return 'KeyValueObject'
1869
2145
  }
1870
2146
  static get _superclass() {
1871
- return 'Repo'
2147
+ return 'KeyValueObject'
1872
2148
  }
1873
2149
 
1874
- get _classname() {
1875
- return 'Repo'
2150
+ static addItem(arr, key, value) {
2151
+ arr.push(this.init({ key, value }))
1876
2152
  }
1877
-
1878
- get _superclass() {
1879
- return 'Repo'
2153
+ static addRecord(arr = [], key, value) {
2154
+ if (!this.hasKeyValue(arr, key, value)) {
2155
+ arr.push(this.init({ key, value }))
2156
+ }
2157
+ return arr
1880
2158
  }
1881
-
1882
- get isValid() {
1883
- return this.model
1884
- && (typeof this.model.deleteOne === 'function')
1885
- && (typeof this.model.findAll === 'function')
1886
- && (typeof this.model.saveOne === 'function')
2159
+ static appendRecord(arr = [], key, value) {
2160
+ return arr.map((item) => {
2161
+ if (this.sameKey(item, key)) {
2162
+ item.value = [...item.value, ...value]
2163
+ }
2164
+ return item
2165
+ })
1887
2166
  }
1888
-
1889
- get queryOptions() {
1890
- return {
1891
- ...this._sharedOptions,
1892
- ...this._queryOptions,
1893
- }
2167
+ static appendValueArray(arr = [], key, value) {
2168
+ return arr.map((item) => {
2169
+ if (this.sameKey(item, key)) {
2170
+ item.value = [...item.value, ...value]
2171
+ }
2172
+ return item
2173
+ })
1894
2174
  }
1895
-
1896
- get saveOptions() {
1897
- return {
1898
- ...this._sharedOptions,
1899
- ...this._saveOptions,
1900
- }
2175
+ static foundByKey(arr = [], key) {
2176
+ const found = arr.find((m) => {
2177
+ return this.sameKey(m, key)
2178
+ })
2179
+ return found || null
1901
2180
  }
1902
-
1903
- init(options) {
1904
- if (this._Class && typeof this._Class.init === 'function') {
1905
- return this._Class.init(options)
2181
+ static foundValueByKey(arr = [], key) {
2182
+ const found = this.foundByKey(arr, key)
2183
+ return found ? found.value : null
2184
+ }
2185
+ static fromObject(options = {}) {
2186
+ return Object.keys(options).reduce((acc, key) => {
2187
+ acc.push(this.init({ key, value: options[key] }))
2188
+ return acc
2189
+ }, [])
2190
+ }
2191
+ static getValueByKey(arr = [], key) {
2192
+ return this.foundValueByKey(arr, key)
2193
+ }
2194
+ static getValueByKeyFromArray(arr = [], key) {
2195
+ if (arr.length === 0) {
2196
+ return null
1906
2197
  }
1907
- return options
2198
+ const firstArr = arr.shift()
2199
+ const found = firstArr.find((i) => {
2200
+ return this.sameKey(i, key)
2201
+ })
2202
+ if (found && found.value) {
2203
+ return found.value
2204
+ }
2205
+ return this.getValueByKeyFromArray(arr, key)
1908
2206
  }
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: []
2207
+ static getValuesByKey(arr = [], key) {
2208
+ return arr.reduce((acc, item) => {
2209
+ if (this.sameKey(item, key)) {
2210
+ acc.push(item.value)
1917
2211
  }
1918
- } catch (err) {
1919
- throw err
2212
+ return acc
2213
+ }, [])
2214
+ }
2215
+ static hasKeyValue(arr = [], key, value) {
2216
+ if (typeof value === 'undefined') {
2217
+ return arr.filter((item) => this.sameKey(item, key)).length > 0
1920
2218
  }
2219
+ return arr.filter((item) => (this.sameKey(item, key) && _isSame(item.value, value))).length > 0
1921
2220
  }
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
- })
2221
+ static insertOrUpdateRecord(arr = [], key, value) {
2222
+ let copy = [...arr]
2223
+ if (!this.hasKeyValue(arr, key)) {
2224
+ copy.push(this.init({ key, value }))
2225
+ } else {
2226
+ copy = this.updateRecord(arr, key, value)
2227
+ }
2228
+ return copy
1947
2229
  }
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
- }
2230
+ static keys(arr = []) {
2231
+ if (Array.isArray(arr)) {
2232
+ return arr.reduce((acc, item) => {
2233
+ acc.push(item.key)
2234
+ return acc
2235
+ }, [])
2236
+ }
2237
+ return []
2238
+ }
2239
+ static merge(toArr, fromArr) {
2240
+ (fromArr || []).map((from) => {
2241
+ const found = toArr.find((to) => {
2242
+ return to.key === from.key
1973
2243
  })
2244
+ if (found) {
2245
+ found.value = (found.value || []).concat(from.value)
2246
+ } else {
2247
+ toArr.push(from)
2248
+ }
1974
2249
  })
1975
- .catch((err) => {
1976
- log({ level: 'warn', output: err.toString() })
1977
- throw err
1978
- })
2250
+ return toArr
1979
2251
  }
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
2252
+ static removeByKey(arr, key) {
2253
+ return arr.reduce((acc, item) => {
2254
+ if (!this.sameKey(item, key)) {
2255
+ acc.push(item)
2006
2256
  }
2007
- log({ level: 'info', output: { ...result } })
2008
- return result
2009
- }).catch((err) => {
2010
- log({ level: 'warn', output: err.toString() })
2011
- throw err
2012
- })
2257
+ return acc
2258
+ }, [])
2013
2259
  }
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)
2260
+ static sameKey(item, key) {
2261
+ return _isSame(item.key, key)
2262
+ }
2263
+ static toObject(arr = []) {
2264
+ if (Array.isArray(arr)) {
2265
+ return arr.reduce((acc, item) => {
2266
+ acc[item.key] = item.value
2267
+ return acc
2268
+ }, {})
2269
+ }
2270
+ return {}
2271
+ }
2272
+ static toString(arr = [], delimiter = '; ') {
2273
+ if (Array.isArray(arr)) {
2274
+ return arr.reduce((acc, item) => {
2275
+ acc.push(`${item.key}: ${item.value}`)
2276
+ return acc
2277
+ }, []).join(delimiter)
2278
+ }
2279
+ return ''
2280
+ }
2281
+ static updateRecord(arr = [], key, value) {
2282
+ return arr.map((item) => {
2283
+ if (this.sameKey(item, key)) {
2284
+ return {
2285
+ ...item,
2286
+ value
2030
2287
  }
2031
- })
2288
+ }
2289
+ return item
2032
2290
  })
2033
2291
  }
2034
- }
2035
-
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
- }
2292
+ static updateOrInsertRecord(arr = [], key, value) {
2293
+ return this.insertOrUpdateRecord(arr, key, value)
2294
+ }
2295
+ static updateRecordsFromArray(arr = [], updateArr = []) {
2296
+ if (Array.isArray(arr) && Array.isArray(updateArr)) {
2297
+ const obj1 = this.toObject(arr)
2298
+ const obj2 = this.toObject(updateArr)
2299
+ return this.fromObject({
2300
+ ...obj1,
2301
+ ...obj2
2050
2302
  })
2051
2303
  }
2304
+ return []
2305
+ }
2306
+ static values(arr = []) {
2307
+ if (Array.isArray(arr)) {
2308
+ return arr.reduce((acc, item) => {
2309
+ acc.push(item.value)
2310
+ return acc
2311
+ }, [])
2312
+ }
2313
+ return []
2052
2314
  }
2053
- }
2054
-
2055
2315
 
2316
+ // getters
2317
+ get isValid() {
2318
+ return !!this.key
2319
+ }
2056
2320
 
2057
- ;// ./lib/models/repo/index.js
2321
+ get toObject() {
2322
+ const obj = {}
2323
+ if (this.isValid) {
2324
+ obj[this.key] = this.value
2325
+ }
2326
+ return obj
2327
+ }
2328
+ }
2058
2329
 
2330
+ function _isSame(key1, key2) {
2331
+ return key1 === key2
2332
+ }
2059
2333
 
2060
2334
 
2061
2335
 
2062
- ;// ./lib/models/service/service.js
2336
+ ;// ./lib/models/keyValueObject/index.js
2063
2337
 
2064
2338
 
2065
2339
 
2066
- class Service {
2067
- constructor({ repo }) {
2068
- this.repo = repo
2069
- }
2070
2340
 
2071
- static get _classname() {
2072
- return 'Service'
2073
- }
2074
- static get _superclass() {
2075
- return 'Service'
2076
- }
2341
+ ;// ./lib/models/metadata/metadata.js
2077
2342
 
2078
- deleteOne({ id }) {
2079
- return this.repo.deleteOne({ id })
2080
- .catch(() => {
2081
- throw new Error(`Not found for query: ${id}`)
2082
- })
2083
- }
2084
2343
 
2085
- async findAll({ query = {}, systemLog } = {}) {
2086
- const result = await this.repo.findAll({ query, systemLog })
2087
- return makeApiResponse({
2088
- repo: this.repo,
2089
- result
2090
- })
2091
- }
2092
2344
 
2093
- async findOne({ query = {}, systemLog } = {}) {
2094
- const result = await this.repo.findOne({ query, systemLog })
2095
- return makeApiResponse({
2096
- repo: this.repo,
2097
- result
2098
- })
2099
- }
2345
+ const DELIMITER = '_'
2100
2346
 
2101
- init(options) {
2102
- return this.repo.init(options)
2103
- }
2104
- initFromArray(arr = []) {
2105
- if (Array.isArray(arr)) {
2106
- return arr.map((a) => this.init(a))
2347
+ class Metadata extends KeyValueObject {
2348
+ static init(options = {}) {
2349
+ if (options instanceof this) {
2350
+ return options
2107
2351
  }
2108
- return []
2352
+ const instance = new this({
2353
+ ...options,
2354
+ key: stringFormatter(options.key, DELIMITER),
2355
+ })
2356
+ return instance.isValid ? instance : null
2109
2357
  }
2110
- initOnlyValidFromArray(arr = []) {
2111
- return this.initFromArray(arr).filter((i) => i)
2358
+ static get _classname() {
2359
+ return 'Metadata'
2112
2360
  }
2113
2361
 
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
2362
+ static merge(toArr, fromArr) {
2363
+ (fromArr || []).map((from) => {
2364
+ const found = toArr.find((to) => {
2365
+ return metadata_isSame(to.key, from.key)
2366
+ })
2367
+ if (found) {
2368
+ found.value = (found.value || []).concat(from.value)
2369
+ } else {
2370
+ toArr.push(from)
2371
+ }
2122
2372
  })
2373
+ return toArr
2123
2374
  }
2124
-
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
- })
2133
- }
2134
- return {
2135
- isNew: null,
2136
- data: [],
2137
- err: new Error('doc is not a valid instance')
2138
- }
2375
+ static sameKey(item, key) {
2376
+ return metadata_isSame(item.key, key)
2139
2377
  }
2140
2378
  }
2141
2379
 
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 })
2380
+ function metadata_isSame(key1, key2) {
2381
+ return stringFormatter(key1, DELIMITER) === stringFormatter(key2, DELIMITER)
2150
2382
  }
2151
2383
 
2152
2384
 
2153
2385
 
2154
- ;// ./lib/models/service/index.js
2386
+ ;// ./lib/models/metadata/index.js
2155
2387
 
2156
2388
 
2157
2389
 
2158
2390
 
2159
- ;// ./lib/models/uniqueKeyGenerator/uniqueKeyGenerator.js
2391
+ ;// ./lib/models/qMeta/qMeta.js
2160
2392
 
2161
2393
 
2162
- class UniqueKeyGenerator {
2394
+ const updateAllowedProps = [
2395
+ 'attributes',
2396
+ 'ref'
2397
+ ]
2398
+
2399
+ class QMeta {
2400
+ constructor(options = {}) {
2401
+ options = options || {}
2402
+ this.attributes = KeyValueObject.initOnlyValidFromArray(options.attributes)
2403
+ this.ref = options.ref || {}
2404
+ }
2405
+
2163
2406
  static get _classname() {
2164
- return 'UniqueKeyGenerator'
2407
+ return 'QMeta'
2165
2408
  }
2166
2409
  static get _superclass() {
2167
- return 'UniqueKeyGenerator'
2410
+ return 'QMeta'
2168
2411
  }
2169
- static makeFormatter({ fieldName, format, options }) {
2170
- switch (format) {
2171
- case 'set_code':
2172
- return _makeSetCode(fieldName, options)
2173
- default:
2174
- return _makeSetCode(fieldName, options)
2412
+
2413
+ // Class methods
2414
+ static init(options = {}) {
2415
+ if (options instanceof QMeta) {
2416
+ return options
2175
2417
  }
2418
+ return new QMeta(options)
2176
2419
  }
2177
- static makeGenerator(arr) {
2178
- const fns = arr.map((item) => this.makeFormatter(item))
2179
- return async (obj) => {
2180
- const output = await pReduce(fns, async (acc, fn) => {
2181
- const _obj = await fn(obj)
2182
- return Object.assign(acc, _obj)
2183
- }, obj)
2184
- return output
2420
+
2421
+ // instance methods
2422
+ addAttribute(obj) {
2423
+ const kvObject = KeyValueObject.init(obj)
2424
+ if (!kvObject) {
2425
+ throw new Error('invalid meta attribute')
2185
2426
  }
2427
+ this.attributes.push(kvObject)
2428
+ return this
2186
2429
  }
2187
- }
2188
2430
 
2189
- function _makeSetCode(fieldName, options) {
2190
- return async (obj = {}) => {
2191
- if (obj[fieldName]) {
2192
- return {}
2193
- }
2194
- return {
2195
- [fieldName]: stringHelper.setCode()
2196
- }
2431
+ update(obj) {
2432
+ Object.keys(obj).forEach((key) => {
2433
+ if (updateAllowedProps.includes(key)) {
2434
+ if (key === 'attributes') {
2435
+ this[key] = KeyValueObject.initOnlyValidFromArray(obj[key])
2436
+ } else {
2437
+ this[key] = obj[key]
2438
+ }
2439
+ }
2440
+ })
2441
+ return this
2197
2442
  }
2198
2443
  }
2199
2444
 
2200
2445
 
2201
2446
 
2202
- ;// ./lib/models/uniqueKeyGenerator/index.js
2203
-
2204
-
2205
-
2206
-
2207
- ;// ./lib/models/index.js
2208
-
2209
-
2210
-
2211
-
2447
+ ;// ./lib/models/qMeta/index.js
2212
2448
 
2213
2449
 
2214
2450
 
2215
2451
 
2452
+ ;// ./lib/models/repo/index.js
2216
2453
 
2217
- ;// ./lib/helpers/generalPost/generalPost.js
2218
2454
 
2219
2455
 
2220
2456
 
2457
+ ;// ./lib/models/service/index.js
2221
2458
 
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
2459
 
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
2460
 
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
2461
 
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
- }
2462
+ ;// ./lib/models/trackedEntity/trackedEntity.js
2260
2463
 
2261
2464
 
2465
+ class TrackedEntity {
2466
+ constructor(options = {}, { trackFlat = false } = {}) {
2467
+ const timestamp = Date.now()
2468
+ const _tracking = {
2469
+ active: options.active ?? true,
2470
+ created: options.created ?? timestamp,
2471
+ creator: options.creator ?? '',
2472
+ deleted: options.deleted ?? false,
2473
+ modified: options.modified ?? timestamp,
2474
+ owner: options.owner ?? '',
2475
+ }
2262
2476
 
2263
- ;// ./lib/helpers/generalPost/index.js
2477
+ if (trackFlat) {
2478
+ Object.assign(this, _tracking)
2479
+ } else {
2480
+ this.meta = { ..._tracking, ...options.meta }
2481
+ }
2482
+ }
2264
2483
 
2484
+ // Class methods
2485
+ static get _classname() {
2486
+ return 'TrackedEntity'
2487
+ }
2488
+ static get _superclass() {
2489
+ return 'TrackedEntity'
2490
+ }
2265
2491
 
2492
+ static init(options = {}) {
2493
+ return init(this, options)
2494
+ }
2495
+ static initFromArray(arr = []) {
2496
+ return initFromArray(this, arr)
2497
+ }
2498
+ static initOnlyValidFromArray(arr = []) {
2499
+ return initOnlyValidFromArray(this, arr)
2500
+ }
2501
+ static nest(entity) {
2502
+ const { active, created, creator, deleted, modified, owner, ...rest } = entity
2503
+ return { ...rest, meta: { active, created, creator, deleted, modified, owner } }
2504
+ }
2266
2505
 
2506
+ // getters
2507
+ get isValid() {
2508
+ return !!this
2509
+ }
2267
2510
 
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)
2511
+ setModified() {
2512
+ const timestamp = Date.now()
2513
+ if (this.meta) {
2514
+ this.meta.modified = timestamp
2515
+ } else {
2516
+ this.modified = timestamp
2517
+ }
2273
2518
  }
2274
- return num
2275
2519
  }
2276
2520
 
2521
+ ;// ./lib/models/trackedEntity/index.js
2277
2522
 
2523
+ // Explicit named export (optional)
2278
2524
 
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
2525
+ ;// ./lib/models/tenantAwareEntity/tenantAwareEntity.js
2290
2526
 
2291
2527
 
2528
+ class TenantAwareEntity extends TrackedEntity {
2529
+ constructor(options = {}) {
2530
+ options = options || {}
2292
2531
 
2532
+ /**
2533
+ * instead of throw error, we choose to implement the isValid checking
2534
+ */
2535
+ // if (!options.tenantCode) {
2536
+ // throw new Error('tenantCode required')
2537
+ // }
2293
2538
 
2294
- ;// ./lib/helpers/stringHelper/stringHelper.js
2295
- function baseXEncode(num, base = 34) {
2296
- const charset = getBaseCharset(base)
2297
- return encode(num, charset)
2298
- }
2539
+ super(options)
2299
2540
 
2300
- function encode(int, charset) {
2301
- let byCode = charset.byCode;
2302
- if (int === 0) {
2303
- return byCode[0];
2541
+ this._tenant = options._tenant
2542
+ this.tenantCode = options.tenantCode // Required for multi-tenancy
2304
2543
  }
2305
2544
 
2306
- var res = "",
2307
- max = charset.length;
2308
- while (int > 0) {
2309
- res = byCode[int % max] + res;
2310
- int = Math.floor(int / max);
2545
+ // Class methods
2546
+ static get _classname() {
2547
+ return 'TenantAwareEntity'
2311
2548
  }
2312
- return res;
2313
- }
2314
-
2315
- function getBaseCharset(base) {
2316
- let charset = '9876543210ABCDEFGHJKLMNPQRSTUVWXYZ'
2317
- if (base === 58) {
2318
- charset = '9876543210ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnpqrstuvwxyz'
2549
+ static get _superclass() {
2550
+ return 'TenantAwareEntity'
2319
2551
  }
2320
- return indexCharset(charset)
2321
- }
2322
2552
 
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;
2553
+ // getters
2554
+ get isValid() {
2555
+ return super.isValid && !!this.tenantCode // Required for multi-tenancy
2332
2556
  }
2333
- return { byCode: byCode, byChar: byChar, length: length };
2334
2557
  }
2335
2558
 
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
2559
+ ;// ./lib/models/tenantAwareEntity/index.js
2560
+
2561
+
2562
+
2563
+ ;// ./lib/models/uniqueKeyGenerator/uniqueKeyGenerator.js
2564
+
2565
+
2566
+ class UniqueKeyGenerator {
2567
+ static get _classname() {
2568
+ return 'UniqueKeyGenerator'
2344
2569
  }
2345
- if (pattern.includes('a')) {
2346
- str += a
2570
+ static get _superclass() {
2571
+ return 'UniqueKeyGenerator'
2347
2572
  }
2348
- if (pattern.includes('1')) {
2349
- str += num
2573
+ static makeFormatter({ fieldName, format, options }) {
2574
+ switch (format) {
2575
+ case 'set_code':
2576
+ return _makeSetCode(fieldName, options)
2577
+ default:
2578
+ return _makeSetCode(fieldName, options)
2579
+ }
2350
2580
  }
2351
- if (pattern.includes('#')) {
2352
- str += mark
2581
+ static makeGenerator(arr) {
2582
+ const fns = arr.map((item) => this.makeFormatter(item))
2583
+ return async (obj) => {
2584
+ const output = await pReduce(fns, async (acc, fn) => {
2585
+ const _obj = await fn(obj)
2586
+ return Object.assign(acc, _obj)
2587
+ }, obj)
2588
+ return output
2589
+ }
2353
2590
  }
2354
- const chars = [...str]
2355
- return [...Array(len)].map(i => {
2356
- return chars[(Math.random() * chars.length) | 0]
2357
- }).join``
2358
2591
  }
2359
2592
 
2360
- function reverse(str) {
2361
- if (typeof str !== 'string') {
2362
- str = str.toString()
2593
+ function _makeSetCode(fieldName, options) {
2594
+ return async (obj = {}) => {
2595
+ if (obj[fieldName]) {
2596
+ return {}
2597
+ }
2598
+ return {
2599
+ [fieldName]: stringHelper.setCode()
2600
+ }
2363
2601
  }
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
2602
  }
2379
2603
 
2380
- const stringHelper = {
2381
- setCode
2382
- }
2383
-
2384
-
2385
- ;// ./lib/helpers/stringHelper/index.js
2386
2604
 
2387
2605
 
2606
+ ;// ./lib/models/uniqueKeyGenerator/index.js
2388
2607
 
2389
2608
 
2390
2609
 
2391
- ;// ./lib/helpers/index.js
2392
2610
 
2611
+ ;// ./lib/models/index.js
2393
2612
 
2394
2613
 
2395
2614