@questwork/q-utilities 0.1.2 → 0.1.4

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.
@@ -52,13 +52,13 @@ __webpack_require__.d(__webpack_exports__, {
52
52
  ApiResponse: () => (/* reexport */ ApiResponse),
53
53
  KeyValueObject: () => (/* reexport */ KeyValueObject),
54
54
  Metadata: () => (/* reexport */ Metadata),
55
+ QMeta: () => (/* reexport */ QMeta),
55
56
  Repo: () => (/* reexport */ Repo),
56
57
  Service: () => (/* reexport */ Service),
57
58
  convertString: () => (/* reexport */ convertString),
58
59
  formatDate: () => (/* reexport */ formatDate),
59
60
  getValidation: () => (/* reexport */ getValidation),
60
61
  getValueByKeys: () => (/* reexport */ getValueByKeys),
61
- jwtHelper: () => (/* reexport */ jwtHelper),
62
62
  makeApiResponse: () => (/* reexport */ makeApiResponse),
63
63
  makeService: () => (/* reexport */ makeService),
64
64
  padZeros: () => (/* reexport */ padZeros),
@@ -289,284 +289,6 @@ function getValueByKeys(keys, data) {
289
289
  ;// ./lib/helpers/getValueByKeys/index.js
290
290
 
291
291
 
292
- ;// ./lib/helpers/jwtHelper/jwtHelper.js
293
- // 'use strict'
294
-
295
- /* eslint-disable new-cap */
296
- /* eslint-disable camelcase */
297
- /* eslint-disable no-mixed-operators */
298
- /* eslint-disable no-useless-escape */
299
- /* eslint-disable no-param-reassign */
300
-
301
- /* eslint func-names: 0 */
302
-
303
- // const Buffer = require('buffer/').Buffer
304
-
305
- const EXPIRY = 3600 // in second
306
- const ALGORITHM = 'HS256'
307
- const SECRET = 'ab1234cd'
308
-
309
- const _hasBuffer = typeof Buffer === 'function'
310
-
311
- const jwtHelper = {
312
- create(obj, { secret, algorithm, expiry } = {}) {
313
- const sAlgorithm = algorithm || ALGORITHM
314
- const sSecret = secret || SECRET
315
- const exp = expiry || getTimeInSecond() + EXPIRY
316
- const payload = {
317
- ...obj,
318
- exp
319
- }
320
- return encode(payload, sSecret, sAlgorithm)
321
- },
322
- createByUser(loginAccount) {
323
- const exp = getTimeInSecond() + EXPIRY
324
- const payload = {
325
- loginAccount,
326
- exp
327
- }
328
- return this.encode(payload)
329
- },
330
- encode(payload, algorithm) {
331
- return encode(payload, SECRET, algorithm)
332
- },
333
- decode(token, algorithm) {
334
- const noVerify = !this.verify(token)
335
- return decode(token, SECRET, noVerify, algorithm) // if noVerify = true, may skip verification
336
- },
337
- getPayload(token) {
338
- const payload = getPayload(token)
339
- return {
340
- payload
341
- }
342
- },
343
- getPayloadIdByKey(token, key) {
344
- const payload = getPayload(token)
345
- const id = payload[key] ? payload[key].id : null
346
- return {
347
- id,
348
- jwtToken: token
349
- }
350
- },
351
- resolve(token, secret, algorithm) {
352
- const sSecret = secret || SECRET
353
- return decode(token, sSecret, false, algorithm) // need verification
354
- },
355
- verify(token) {
356
- const payload = getPayload(token)
357
- const today = getTimeInSecond()
358
- return (payload.exp && (today <= payload.exp)) || false
359
- }
360
- }
361
-
362
- /**
363
- * Private functions
364
- */
365
-
366
- const getPayload = (token) => decode(token, SECRET, true)
367
-
368
- const getTimeInSecond = () => Math.floor(Date.now() / 1000)
369
-
370
- /**
371
- * Private functions, based on jwt-simple 0.2.0
372
- */
373
-
374
- const { cryptoHelper } = require('../cryptoHelper')
375
-
376
- const algorithmMap = {
377
- HS256: 'sha256',
378
- HS384: 'sha384',
379
- HS512: 'sha512',
380
- RS256: 'RSA-SHA256'
381
- }
382
-
383
- const typeMap = {
384
- HS256: 'hmac',
385
- HS384: 'hmac',
386
- HS512: 'hmac',
387
- RS256: 'sign'
388
- }
389
-
390
-
391
- /**
392
- * Decode jwt
393
- *
394
- * @param {Object} token
395
- * @param {String} key
396
- * @param {Boolean} noVerify
397
- * @param {String} algorithm
398
- * @return {Object} payload
399
- * @api public
400
- */
401
- const decode = function jwt_decode(token, key, noVerify, algorithm) {
402
- // check token
403
- if (!token) {
404
- throw new Error('No token supplied')
405
- }
406
- // check segments
407
- const segments = token.split('.')
408
- if (segments.length !== 3) {
409
- throw new Error('Not enough or too many segments')
410
- }
411
-
412
- // All segment should be base64
413
- const headerSeg = segments[0]
414
- const payloadSeg = segments[1]
415
- const signatureSeg = segments[2]
416
-
417
- // base64 decode and parse JSON
418
- const header = JSON.parse(base64urlDecode(headerSeg))
419
-
420
- const payload = JSON.parse(base64urlDecode(payloadSeg))
421
-
422
- if (!noVerify) {
423
- const signingMethod = algorithmMap[algorithm || header.alg]
424
- const signingType = typeMap[algorithm || header.alg]
425
- if (!signingMethod || !signingType) {
426
- throw new Error('Algorithm not supported')
427
- }
428
-
429
- // verify signature. `sign` will return base64 string.
430
- const signingInput = [headerSeg, payloadSeg].join('.')
431
- if (!verify(signingInput, key, signingMethod, signingType, signatureSeg)) {
432
- throw new Error('Signature verification failed')
433
- }
434
- }
435
-
436
- return payload
437
- }
438
-
439
-
440
- /**
441
- * Encode jwt
442
- *
443
- * @param {Object} payload
444
- * @param {String} key
445
- * @param {String} algorithm
446
- * @return {String} token
447
- * @api public
448
- */
449
- const encode = function jwt_encode(payload, key, algorithm) {
450
- // Check key
451
- if (!key) {
452
- throw new Error('Require key')
453
- }
454
-
455
- // Check algorithm, default is HS256
456
- if (!algorithm) {
457
- algorithm = ALGORITHM
458
- }
459
-
460
- const signingMethod = algorithmMap[algorithm]
461
- const signingType = typeMap[algorithm]
462
- if (!signingMethod || !signingType) {
463
- throw new Error('Algorithm not supported')
464
- }
465
-
466
- // header, typ is fixed value.
467
- const header = { typ: 'JWT', alg: algorithm }
468
-
469
- // create segments, all segments should be base64 string
470
- const segments = []
471
- segments.push(base64urlEncode(JSON.stringify(header)))
472
- segments.push(base64urlEncode(JSON.stringify(payload)))
473
- segments.push(sign(segments.join('.'), key, signingMethod, signingType))
474
-
475
- return segments.join('.')
476
- }
477
-
478
-
479
- /**
480
- * private util functions
481
- */
482
-
483
- function verify(input, key, method, type, signature) {
484
- if (type === 'hmac') {
485
- return (signature === sign(input, key, method, type))
486
- } else if (type === 'sign') {
487
- try {
488
- return cryptoHelper.createStringVerify({
489
- algorithm: method,
490
- data: input,
491
- object: key,
492
- signature: base64urlUnescape(signature),
493
- signatureEncoding: 'base64'
494
- })
495
- } catch (error) {
496
- throw new Error('createStringVerify failed')
497
- }
498
- }
499
- throw new Error('Algorithm type not recognized')
500
- }
501
-
502
- function sign(input, key, method, type) {
503
- let base64str
504
- if (type === 'hmac') {
505
- base64str = cryptoHelper.createStringHmac({
506
- algorithm: method,
507
- key,
508
- data: input,
509
- outputEncoding: 'base64'
510
- })
511
- } else if (type === 'sign') {
512
- try {
513
- base64str = cryptoHelper.createSignature({
514
- algorithm: method,
515
- data: input,
516
- privateKey: key,
517
- outputEncoding: 'base64'
518
- })
519
- } catch (error) {
520
- throw new Error('createSignature failed')
521
- }
522
- } else {
523
- throw new Error('Algorithm type not recognized')
524
- }
525
- return base64urlEscape(base64str)
526
- }
527
-
528
- function _decode(str) {
529
- if (_hasBuffer) {
530
- return Buffer.from(base64urlUnescape(str), 'base64').toString('utf8')
531
- }
532
- return atob(base64urlUnescape(str))
533
- }
534
-
535
- function _encode(str) {
536
- if (_hasBuffer) {
537
- return base64urlEscape(Buffer.from(str, 'utf8').toString('base64'))
538
- }
539
- return base64urlEscape(btoa(str))
540
- }
541
-
542
- function base64urlDecode(str) {
543
- // fixed bug if decode string is incorrect
544
- // return (new Buffer.from(base64urlUnescape(str), 'base64')).toString() || '{}'
545
- return _decode(str)
546
- }
547
-
548
- function base64urlUnescape(str) {
549
- str += new Array(5 - str.length % 4).join('=')
550
- return str.replace(/\-/g, '+').replace(/_/g, '/')
551
- }
552
-
553
- function base64urlEncode(str) {
554
- // return base64urlEscape(new Buffer.from((str)).toString('base64'))
555
- return _encode(str)
556
- }
557
-
558
- function base64urlEscape(str) {
559
- return str.replace(/\+/g, '-').replace(/\//g, '_').replace(/=/g, '')
560
- }
561
-
562
-
563
-
564
- ;// ./lib/helpers/jwtHelper/index.js
565
-
566
-
567
-
568
-
569
-
570
292
  ;// ./lib/helpers/padZeros/padZeros.js
571
293
  function padZeros(num, minLength = 6) {
572
294
  num = num.toString()
@@ -603,7 +325,6 @@ function stringFormatter(str) {
603
325
 
604
326
 
605
327
 
606
-
607
328
  ;// ./lib/models/apiResponse/apiResponse.js
608
329
  class ApiResponse {
609
330
  constructor(options = {}) {
@@ -694,107 +415,85 @@ class KeyValueObject {
694
415
  }
695
416
 
696
417
  static addItem(arr, key, value) {
697
- arr.push(
698
- { key, value }
699
- )
418
+ arr.push(this.init({ key, value }))
700
419
  }
701
420
  static addRecord(arr = [], key, value) {
702
- const self = this
703
421
  if (!this.hasKeyValue(arr, key, value)) {
704
- arr.push(self.init({ key, value }))
422
+ arr.push(this.init({ key, value }))
705
423
  }
706
424
  return arr
707
425
  }
708
-
709
426
  static appendRecord(arr = [], key, value) {
710
427
  return arr.map((item) => {
711
- if (item.key === key) {
428
+ if (this.sameKey(item, key)) {
712
429
  item.value = [...item.value, ...value]
713
430
  }
714
431
  return item
715
432
  })
716
433
  }
717
-
718
- static fromObject(options = {}) {
719
- const self = this
720
- return Object.keys(options).reduce((acc, key) => {
721
- acc.push(self.init({ key, value: options[key] }))
722
- return acc
723
- }, [])
724
- }
725
-
726
- static removeByKey(arr, key) {
727
- return arr.reduce((acc, item) => {
728
- if (item.key !== key) {
729
- acc.push(item)
434
+ static appendValueArray(arr = [], key, value) {
435
+ return arr.map((item) => {
436
+ if (this.sameKey(item, key)) {
437
+ item.value = [...item.value, ...value]
730
438
  }
731
- return acc
732
- }, [])
439
+ return item
440
+ })
733
441
  }
734
-
735
442
  static foundByKey(arr = [], key) {
736
443
  const found = arr.find((m) => {
737
- return m.key === key
444
+ return this.sameKey(m, key)
738
445
  })
739
446
  return found || null
740
447
  }
741
-
742
448
  static foundValueByKey(arr = [], key) {
743
449
  const found = this.foundByKey(arr, key)
744
450
  return found ? found.value : null
745
451
  }
746
-
452
+ static fromObject(options = {}) {
453
+ return Object.keys(options).reduce((acc, key) => {
454
+ acc.push(this.init({ key, value: options[key] }))
455
+ return acc
456
+ }, [])
457
+ }
747
458
  static getValueByKey(arr = [], key) {
748
- const found = arr.find((i) => {
749
- return i.key === key
750
- })
751
- if (found) {
752
- return found.value
753
- }
754
- return null
459
+ return this.foundValueByKey(arr, key)
755
460
  }
756
-
757
461
  static getValueByKeyFromArray(arr = [], key) {
758
462
  if (arr.length === 0) {
759
463
  return null
760
464
  }
761
465
  const firstArr = arr.shift()
762
466
  const found = firstArr.find((i) => {
763
- return i.key === key
467
+ return this.sameKey(i, key)
764
468
  })
765
469
  if (found && found.value) {
766
470
  return found.value
767
471
  }
768
472
  return this.getValueByKeyFromArray(arr, key)
769
473
  }
770
-
771
474
  static getValuesByKey(arr = [], key) {
772
475
  return arr.reduce((acc, item) => {
773
- if (item.key === key) {
476
+ if (this.sameKey(item, key)) {
774
477
  acc.push(item.value)
775
478
  }
776
479
  return acc
777
480
  }, [])
778
481
  }
779
-
780
482
  static hasKeyValue(arr = [], key, value) {
781
483
  if (typeof value === 'undefined') {
782
- return arr.filter((item) => item.key === key).length > 0
484
+ return arr.filter((item) => this.sameKey(item, key)).length > 0
783
485
  }
784
- return arr.filter((item) => (item.key === key && item.value === value)).length > 0
486
+ return arr.filter((item) => (this.sameKey(item, key) && item.value === value)).length > 0
785
487
  }
786
-
787
488
  static insertOrUpdateRecord(arr = [], key, value) {
788
- const self = this
789
489
  let copy = [...arr]
790
- if (!self.hasKeyValue(arr, key)) {
791
- copy.push(self.init({ key, value }))
490
+ if (!this.hasKeyValue(arr, key)) {
491
+ copy.push(this.init({ key, value }))
792
492
  } else {
793
- copy = self.updateRecord(arr, key, value)
493
+ copy = this.updateRecord(arr, key, value)
794
494
  }
795
495
  return copy
796
496
  }
797
-
798
497
  static keys(arr = []) {
799
498
  if (Array.isArray(arr)) {
800
499
  return arr.reduce((acc, item) => {
@@ -804,7 +503,6 @@ class KeyValueObject {
804
503
  }
805
504
  return []
806
505
  }
807
-
808
506
  static merge(toArr, fromArr) {
809
507
  (fromArr || []).map((from) => {
810
508
  const found = toArr.find((to) => {
@@ -818,7 +516,17 @@ class KeyValueObject {
818
516
  })
819
517
  return toArr
820
518
  }
821
-
519
+ static removeByKey(arr, key) {
520
+ return arr.reduce((acc, item) => {
521
+ if (!this.sameKey(item, key)) {
522
+ acc.push(item)
523
+ }
524
+ return acc
525
+ }, [])
526
+ }
527
+ static sameKey(item, key) {
528
+ return item.key === key
529
+ }
822
530
  static toObject(arr = []) {
823
531
  if (Array.isArray(arr)) {
824
532
  return arr.reduce((acc, item) => {
@@ -828,7 +536,6 @@ class KeyValueObject {
828
536
  }
829
537
  return {}
830
538
  }
831
-
832
539
  static toString(arr = [], delimiter = '; ') {
833
540
  if (Array.isArray(arr)) {
834
541
  return arr.reduce((acc, item) => {
@@ -838,10 +545,9 @@ class KeyValueObject {
838
545
  }
839
546
  return ''
840
547
  }
841
-
842
548
  static updateRecord(arr = [], key, value) {
843
549
  return arr.map((item) => {
844
- if (item.key === key) {
550
+ if (this.sameKey(item, key)) {
845
551
  return {
846
552
  ...item,
847
553
  value
@@ -850,11 +556,9 @@ class KeyValueObject {
850
556
  return item
851
557
  })
852
558
  }
853
-
854
559
  static updateOrInsertRecord(arr = [], key, value) {
855
560
  return this.insertOrUpdateRecord(arr, key, value)
856
561
  }
857
-
858
562
  static updateRecordsFromArray(arr = [], updateArr = []) {
859
563
  if (Array.isArray(arr) && Array.isArray(updateArr)) {
860
564
  const obj1 = this.toObject(arr)
@@ -866,7 +570,6 @@ class KeyValueObject {
866
570
  }
867
571
  return []
868
572
  }
869
-
870
573
  static values(arr = []) {
871
574
  if (Array.isArray(arr)) {
872
575
  return arr.reduce((acc, item) => {
@@ -913,22 +616,92 @@ class Metadata extends KeyValueObject {
913
616
  })
914
617
  return instance.isValid ? instance : null
915
618
  }
619
+ static get _classname() {
620
+ return 'Metadata'
621
+ }
916
622
 
917
- static foundByKey(arr = [], key) {
918
- const found = (arr || []).find((m) => {
919
- return m.key === stringFormatter(key)
623
+ static merge(toArr, fromArr) {
624
+ (fromArr || []).map((from) => {
625
+ const found = toArr.find((to) => {
626
+ return stringFormatter(to.key) === stringFormatter(from.key)
627
+ })
628
+ if (found) {
629
+ found.value = (found.value || []).concat(from.value)
630
+ } else {
631
+ toArr.push(from)
632
+ }
920
633
  })
921
- return found || null
634
+ return toArr
635
+ }
636
+ static sameKey(item, key) {
637
+ return stringFormatter(item.key) === stringFormatter(key)
638
+ }
639
+ }
640
+
641
+
642
+
643
+ ;// ./lib/models/metadata/index.js
644
+
645
+
646
+
647
+
648
+ ;// ./lib/models/qMeta/qMeta.js
649
+
650
+
651
+ const updateAllowedProps = [
652
+ 'attributes',
653
+ 'ref'
654
+ ]
655
+
656
+ class QMeta {
657
+ constructor(options = {}) {
658
+ options = options || {}
659
+ this.attributes = KeyValueObject.initOnlyValidFromArray(options.attributes)
660
+ this.ref = options.ref || {}
922
661
  }
923
662
 
924
663
  static get _classname() {
925
- return 'Metadata'
664
+ return 'QMeta'
665
+ }
666
+ static get _superclass() {
667
+ return 'QMeta'
668
+ }
669
+
670
+ // Class methods
671
+ static init(options = {}) {
672
+ if (options instanceof QMeta) {
673
+ return options
674
+ }
675
+ return new QMeta(options)
676
+ }
677
+
678
+ // instance methods
679
+ addAttribute(obj) {
680
+ const kvObject = KeyValueObject.init(obj)
681
+ if (!kvObject) {
682
+ throw new Error('invalid meta attribute')
683
+ }
684
+ this.attributes.push(kvObject)
685
+ return this
686
+ }
687
+
688
+ update(obj) {
689
+ Object.keys(obj).forEach((key) => {
690
+ if (updateAllowedProps.includes(key)) {
691
+ if (key === 'attributes') {
692
+ this[key] = KeyValueObject.initOnlyValidFromArray(obj[key])
693
+ } else {
694
+ this[key] = obj[key]
695
+ }
696
+ }
697
+ })
698
+ return this
926
699
  }
927
700
  }
928
701
 
929
702
 
930
703
 
931
- ;// ./lib/models/metadata/index.js
704
+ ;// ./lib/models/qMeta/index.js
932
705
 
933
706
 
934
707
 
@@ -936,10 +709,22 @@ class Metadata extends KeyValueObject {
936
709
  ;// ./lib/models/repo/repo.js
937
710
  class Repo {
938
711
  constructor(options) {
712
+ options = options || {}
939
713
  this.model = options.model
940
- this.dbTransaction = options.dbTransaction
714
+ this._sharedOptions = options._sharedOptions // { session: this.dbTransaction }
715
+ this._queryOptions = options._queryOptions
716
+ this._saveOptions = options._saveOptions
717
+ this._Class = options._constructor && options._constructor._Class
718
+ ? options._constructor._Class
719
+ : null
720
+ }
721
+ static init(options = {}) {
722
+ if (options instanceof this) {
723
+ return options
724
+ }
725
+ const instance = new this(options)
726
+ return instance.isValid ? instance : null
941
727
  }
942
-
943
728
  static get _classname() {
944
729
  return 'Repo'
945
730
  }
@@ -947,8 +732,40 @@ class Repo {
947
732
  return 'Repo'
948
733
  }
949
734
 
735
+ get _classname() {
736
+ return 'Repo'
737
+ }
738
+
739
+ get _superclass() {
740
+ return 'Repo'
741
+ }
742
+
743
+ get isValid() {
744
+ return this.model
745
+ && (typeof this.model.deleteOne === 'function')
746
+ && (typeof this.model.findAll === 'function')
747
+ && (typeof this.model.saveOne === 'function')
748
+ }
749
+
750
+ get queryOptions() {
751
+ return {
752
+ ...this._sharedOptions,
753
+ ...this._queryOptions,
754
+ }
755
+ }
756
+
757
+ get saveOptions() {
758
+ return {
759
+ ...this._sharedOptions,
760
+ ...this._saveOptions,
761
+ }
762
+ }
763
+
950
764
  init(options) {
951
- throw new Error('subclass should implement .init(options)')
765
+ if (this._Class && typeof this._Class.init === 'function') {
766
+ return this._Class.init(options)
767
+ }
768
+ return options
952
769
  }
953
770
 
954
771
  async deleteOne({ id }) {
@@ -965,9 +782,8 @@ class Repo {
965
782
  }
966
783
 
967
784
  findAll({ query }) {
968
- const options = {}
969
785
  return new Promise((resolve, reject) => {
970
- this.model.findAll(query, options, (err, data, total) => {
786
+ this.model.findAll(query, this.queryOptions, (err, data, total) => {
971
787
  if (err) {
972
788
  reject(err)
973
789
  } else {
@@ -982,9 +798,8 @@ class Repo {
982
798
  }
983
799
 
984
800
  findOne({ query }) {
985
- const options = { session: this.dbTransaction }
986
801
  return new Promise((resolve, reject) => {
987
- this.model.findAll(query, options, (err, data) => {
802
+ this.model.findAll(query, this.queryOptions, (err, data) => {
988
803
  if (err) {
989
804
  reject(err)
990
805
  } else if (data.length === 1) {
@@ -1003,13 +818,13 @@ class Repo {
1003
818
  }
1004
819
 
1005
820
  saveAll({ docs }) {
1006
- const self = this
1007
821
  let isNew
1008
822
  return Promise.all(docs.map(async (doc) => {
1009
823
  if (doc) {
1010
- const result = await self.saveOne({ doc })
824
+ const result = await this.saveOne({ doc })
1011
825
  isNew = result.isNew
1012
- return result.data[0]
826
+ const _data = result._data || result.data
827
+ return _data[0]
1013
828
  }
1014
829
  return null
1015
830
  })).then((savedData) => {
@@ -1023,9 +838,8 @@ class Repo {
1023
838
  }
1024
839
 
1025
840
  saveOne({ doc }) {
1026
- const options = { session: this.dbTransaction }
1027
841
  return new Promise((resolve, reject) => {
1028
- this.model.saveOne(doc, options, (err, result) => {
842
+ this.model.saveOne(doc, this.saveOptions, (err, result) => {
1029
843
  if (err) {
1030
844
  reject(err)
1031
845
  } else {
@@ -1121,9 +935,9 @@ function makeService({ repo }) {
1121
935
  if (repo === undefined) {
1122
936
  throw new Error('repo is required.')
1123
937
  }
1124
- // if (!(repo instanceof Repo)) {
1125
- // throw new Error('repo is not an instance of Repo.')
1126
- // }
938
+ if (repo._superclass !== Repo._superclass) {
939
+ throw new Error('repo is not an instance of Repo.')
940
+ }
1127
941
  return new Service({ repo })
1128
942
  }
1129
943
 
@@ -1141,6 +955,7 @@ function makeService({ repo }) {
1141
955
 
1142
956
 
1143
957
 
958
+
1144
959
  ;// ./lib/index.js
1145
960
 
1146
961