bajo-extra 0.2.10 → 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.
- package/bajo/helper/fetch-and-save.js +41 -93
- package/bajo/helper/fetch-bulk.js +77 -0
- package/package.json +2 -1
|
@@ -1,104 +1,52 @@
|
|
|
1
|
-
async function
|
|
2
|
-
const {
|
|
3
|
-
const {
|
|
4
|
-
const {
|
|
5
|
-
const
|
|
6
|
-
const
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
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]
|
|
11
15
|
}
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
spin.setText('Got %d records, processing...', iterator.length)
|
|
20
|
-
for (let r of iterator) {
|
|
21
|
-
await setImmediate()
|
|
22
|
-
if (converter) r = await converter.call(this, r, options)
|
|
23
|
-
if (isEmpty(r)) {
|
|
24
|
-
stat.skipped++
|
|
25
|
-
continue
|
|
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'
|
|
26
23
|
}
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
const body = options.updateConverter ? await options.updateConverter.call(this, r, options) : r
|
|
40
|
-
record = await recordUpdate(coll, existing.id, body)
|
|
41
|
-
method = 'updated'
|
|
42
|
-
stat.updated++
|
|
43
|
-
} else {
|
|
44
|
-
stat.skipped++
|
|
45
|
-
print.warn(`[${spin.getElapsed()}] Record %s exists, skipped`, JSON.stringify(r))
|
|
46
|
-
method = 'skipped'
|
|
47
|
-
}
|
|
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)
|
|
48
36
|
} else {
|
|
49
|
-
|
|
50
|
-
record = await recordCreate(coll, r)
|
|
51
|
-
method = 'created'
|
|
52
|
-
}
|
|
53
|
-
if (record && current.coll && current.query) {
|
|
54
|
-
const query = await current.query.call(this, { body: r, record, options })
|
|
55
|
-
const recs = await recordFind(current.coll, { query }, { skipCache: true })
|
|
56
|
-
const rc = current.converter ? await current.converter.call(this, { body: r, record, options }) : r
|
|
57
|
-
if (rc) {
|
|
58
|
-
if (recs.length > 0) {
|
|
59
|
-
const id = recs[0].id
|
|
60
|
-
await recordUpdate(current.coll, id, rc)
|
|
61
|
-
} else {
|
|
62
|
-
await recordCreate(current.coll, rc)
|
|
63
|
-
}
|
|
64
|
-
}
|
|
37
|
+
await recordCreate(current.coll, rc)
|
|
65
38
|
}
|
|
66
|
-
if (options.printCount && options.printCount < count && (count % options.printCount === 0)) print.succeed('[%s] Processed %d/%d', spin.getElapsed(), count, iterator.length)
|
|
67
|
-
else if (!spin.opts.isLog) spin.setText('Record %d/%d...', count, iterator.length, method)
|
|
68
|
-
count++
|
|
69
|
-
} catch (err) {
|
|
70
|
-
console.log(err)
|
|
71
|
-
spin.setText(validationErrorMessage(err) + ', continue')
|
|
72
39
|
}
|
|
73
40
|
}
|
|
74
|
-
|
|
75
|
-
print.succeed('[%s] Created: %d, Updated: %d, Skipped: %d', spin.getElapsed(), stat.created, stat.updated, stat.skipped)
|
|
76
|
-
return iterator.length
|
|
41
|
+
return method
|
|
77
42
|
}
|
|
78
43
|
|
|
79
|
-
async function fetchAndSave ({
|
|
80
|
-
const {
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
const spin = spinner({ showCounter: true }).start('Fetching starts...')
|
|
86
|
-
let step = 1
|
|
87
|
-
for (;;) {
|
|
88
|
-
print.info('[%s] Fetch batch #%d', spin.getElapsed(), step)
|
|
89
|
-
const newSource = await options.batch.call(this, source)
|
|
90
|
-
if (newSource) source = newSource
|
|
91
|
-
const length = await batching.call(this, { source, converter, coll, current, options, spin })
|
|
92
|
-
if (length === 0) {
|
|
93
|
-
print.info('All done!')
|
|
94
|
-
break
|
|
95
|
-
}
|
|
96
|
-
step++
|
|
97
|
-
}
|
|
98
|
-
} else {
|
|
99
|
-
const spin = spinner({ showCounter: true }).start('Fetching starts...')
|
|
100
|
-
await batching.call(this, { source, converter, coll, current, options, spin })
|
|
101
|
-
}
|
|
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)
|
|
102
50
|
}
|
|
103
51
|
|
|
104
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.
|
|
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",
|