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