bajo-extra 0.2.9 → 0.2.11

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.
@@ -1,47 +1,52 @@
1
- async function fetchAndSave ({ source = {}, converter, coll, current = {}, options = {} } = {}) {
2
- const { setImmediate, importPkg, spinner, print } = this.bajo.helper
3
- const { isEmpty, isFunction } = await importPkg('lodash-es')
4
- const { fetch } = this.bajoExtra.helper
5
- const { recordCreate, recordFind, recordUpdate, validationErrorMessage } = this.bajoDb.helper
6
- const spin = spinner({ showCounter: true }).start('Fetching starts...')
7
- const resp = await fetch(source.url, source.options ?? {})
8
- if (isEmpty(resp)) spin.fatal('No result from server, aborted!')
9
- if (source.abort) {
10
- const aborted = await source.abort.call(this, resp)
11
- if (aborted) spin.fatal(aborted)
1
+ async function handler (rec, bulk) {
2
+ const { importPkg } = this.bajo.helper
3
+ const { isFunction, set } = await importPkg('lodash-es')
4
+ const { recordCreate, recordFind, recordUpdate } = this.bajoDb.helper
5
+ const save = bulk.save ?? {}
6
+ const current = save.current ?? {}
7
+ let existing
8
+ let record
9
+ let method
10
+ save.checkUnique = save.checkUnique ?? 'id'
11
+ if (['unique', 'upsert'].includes(save.mode)) {
12
+ const query = isFunction(save.checkUnique) ? await save.checkUnique.call(this, rec, save) : set({}, save.checkUnique, rec[save.checkUnique])
13
+ const resp = await recordFind(save.coll, { query, limit: 1 }, { skipCache: true })
14
+ if (resp.length > 0) existing = resp[0]
12
15
  }
13
- let count = 0
14
- const iterator = isFunction(source.iterator) ? await source.iterator.call(this, resp) : resp[source.iterator]
15
- spin.setText('Got %d records, processing...', iterator.length)
16
- for (let r of iterator) {
17
- await setImmediate()
18
- if (converter) r = await converter.call(this, r, options)
19
- if (isEmpty(r)) continue
20
- try {
21
- await recordCreate(coll, r)
22
- if (current.coll && current.query) {
23
- const query = await current.query.call(this, r)
24
- const recs = await recordFind(current.coll, { query }, { skipCache: true })
25
- const rc = current.converter ? await current.converter.call(this, r, options) : r
26
- if (rc) {
27
- if (recs.length > 0) {
28
- const id = recs[0].id
29
- await recordUpdate(current.coll, id, rc)
30
- } else {
31
- await recordCreate(current.coll, rc)
32
- }
33
- }
16
+ if (existing) {
17
+ if (save.mode === 'upsert') {
18
+ const body = save.updateConverter ? await save.updateConverter.call(this, rec, save) : rec
19
+ record = await recordUpdate(save.coll, existing.id, body)
20
+ method = 'updated'
21
+ } else {
22
+ method = 'skipped'
23
+ }
24
+ } else {
25
+ record = await recordCreate(save.coll, rec)
26
+ method = 'created'
27
+ }
28
+ if (record && current.coll && current.query) {
29
+ const query = await current.query.call(this, { body: rec, record, opts: save })
30
+ const recs = await recordFind(current.coll, { query }, { skipCache: true })
31
+ const rc = current.converter ? await current.converter.call(this, { body: rec, record, opts: save }) : rec
32
+ if (rc) {
33
+ if (recs.length > 0) {
34
+ const id = recs[0].id
35
+ await recordUpdate(current.coll, id, rc)
36
+ } else {
37
+ await recordCreate(current.coll, rc)
34
38
  }
35
- if (options.printCount && (count % options.printCount === 0)) print.succeed(`[${spin.getElapsed()}] Batch line %d/%d`, count, iterator.length)
36
- else if (!spin.opts.isLog) spin.setText('Record %d/%d...', count, iterator.length)
37
- count++
38
- } catch (err) {
39
- console.log(err)
40
- spin.setText(validationErrorMessage(err) + ', continue')
41
39
  }
42
40
  }
43
- spin.info(`${count}/${iterator.length} records processed`)
44
- spin.succeed('Done!')
41
+ return method
42
+ }
43
+
44
+ async function fetchAndSave ({ url, bulk, save = {}, opts = {} } = {}) {
45
+ const { importPkg } = this.bajo.helper
46
+ const { fetchBulk } = this.bajoExtra.helper
47
+ const { merge } = await importPkg('lodash-es')
48
+ merge(bulk, { handler, save })
49
+ await fetchBulk(url, bulk, opts)
45
50
  }
46
51
 
47
52
  export default fetchAndSave
@@ -0,0 +1,77 @@
1
+ async function fetching ({ url, opts, bulk, spin }) {
2
+ const { setImmediate, importPkg, print } = this.bajo.helper
3
+ const { isEmpty, isFunction, has } = await importPkg('lodash-es')
4
+ const { validationErrorMessage } = this.bajoDb.helper
5
+ const { fetch } = this.bajoExtra.helper
6
+ const resp = await fetch(url, opts ?? {})
7
+ if (isEmpty(resp)) {
8
+ spin.fatal('No result from server, aborted!')
9
+ return -1
10
+ }
11
+ if (bulk.abort) {
12
+ const aborted = await bulk.abort.call(this, resp)
13
+ if (aborted) {
14
+ spin.fatal(aborted)
15
+ return -1
16
+ }
17
+ }
18
+ let count = 0
19
+ const stat = { created: 0, updated: 0, skipped: 0, error: 0 }
20
+ bulk.dataKey = bulk.dataKey ?? 'data'
21
+ const data = isFunction(bulk.dataKey) ? await bulk.dataKey.call(this, resp) : resp[bulk.dataKey]
22
+ if (data.length === 0) {
23
+ print.warn('No records to process, abort')
24
+ return 0
25
+ }
26
+ spin.setText('Got %d records, processing...', data.length)
27
+ for (let r of data) {
28
+ await setImmediate()
29
+ if (bulk.converter) r = await bulk.converter.call(this, r, bulk)
30
+ if (isEmpty(r)) {
31
+ stat.skipped++
32
+ continue
33
+ }
34
+ try {
35
+ const result = await bulk.handler.call(this, r, bulk)
36
+ if (result && has(stat, result)) stat[result]++
37
+ if (bulk.printCount && bulk.printCount < count && (count % bulk.printCount === 0)) print.succeed('[%s] Processed %d/%d', spin.getElapsed(), count, data.length)
38
+ else if (!spin.opts.isLog) spin.setText('Record %d/%d...', count, data.length)
39
+ count++
40
+ } catch (err) {
41
+ console.log(err)
42
+ spin.setText(validationErrorMessage(err) + ', continue')
43
+ }
44
+ }
45
+ print.succeed('[%s] %d/%d records processed', spin.getElapsed(), count, data.length)
46
+ print.succeed('[%s] Created: %d, Updated: %d, Skipped: %d', spin.getElapsed(), stat.created, stat.updated, stat.skipped)
47
+ return data.length
48
+ }
49
+
50
+ async function fetchBulk (url, bulk = {}, opts = {}) {
51
+ const { print, spinner, importPkg, error } = this.bajo.helper
52
+ const { isFunction } = await importPkg('lodash-es')
53
+ opts.params = opts.params ?? {}
54
+ bulk.maxStep = bulk.maxStep ?? 0
55
+ if (!isFunction(bulk.handler)) throw error('A function handler must be provided')
56
+ if (bulk.paramsIncFn && isFunction(bulk.ParamsFn)) {
57
+ print.info('Bulk fetch starting')
58
+ const spin = spinner({ showCounter: true }).start('Fetching starts...')
59
+ let step = 1
60
+ for (;;) {
61
+ print.info('[%s] Batch #%d', spin.getElapsed(), step)
62
+ const newOpts = await bulk.paramsIncFn.call(this, { url, bulk, opts })
63
+ if (newOpts) opts = newOpts
64
+ const length = await fetching.call(this, { url, bulk, opts, spin })
65
+ if (length === 0 || (bulk.maxStep > 0 && step >= bulk.maxStep)) {
66
+ print.info('All done!')
67
+ break
68
+ }
69
+ step++
70
+ }
71
+ } else {
72
+ const spin = spinner({ showCounter: true }).start('Fetching starts...')
73
+ await fetching.call(this, { url, bulk, opts, spin })
74
+ }
75
+ }
76
+
77
+ export default fetchBulk
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "bajo-extra",
3
- "version": "0.2.9",
3
+ "version": "0.2.11",
4
4
  "description": "Extra package for Bajo Framework",
5
5
  "main": "index.js",
6
6
  "scripts": {
@@ -32,6 +32,7 @@
32
32
  "email-addresses": "^5.0.0",
33
33
  "fast-jwt": "^3.2.0",
34
34
  "fast-xml-parser": "^4.3.2",
35
+ "littlehash": "^1.0.1",
35
36
  "ndjson-csv-xlsx": "^1.1.1",
36
37
  "performant-array-to-tree": "^1.11.0",
37
38
  "query-string": "^8.1.0",