bajo-extra 0.2.8 → 0.2.10
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 +74 -22
- package/bajoCli/tool/export-to.js +11 -16
- package/bajoCli/tool/import-from.js +12 -20
- package/bajoCli/tool.js +2 -2
- package/package.json +1 -1
|
@@ -1,32 +1,59 @@
|
|
|
1
|
-
async function
|
|
2
|
-
const {
|
|
3
|
-
const { isEmpty, isFunction } = await importPkg('lodash-es')
|
|
1
|
+
async function batching ({ source = {}, converter, coll, current = {}, options = {}, spin } = {}) {
|
|
2
|
+
const { setImmediate, importPkg, print } = this.bajo.helper
|
|
3
|
+
const { isEmpty, isFunction, set } = await importPkg('lodash-es')
|
|
4
4
|
const { fetch } = this.bajoExtra.helper
|
|
5
5
|
const { recordCreate, recordFind, recordUpdate, validationErrorMessage } = this.bajoDb.helper
|
|
6
|
-
const config = getConfig()
|
|
7
|
-
const opts = { type: options.returnEarly ? 'log' : 'bora', pkg: options.pkg }
|
|
8
|
-
const showDatetime = !config.tool
|
|
9
|
-
const spinner = print.bora('Fetching starts...', { showCounter: true, showDatetime, isSilent: options.returnEarly }).start()
|
|
10
|
-
if (options.returnEarly) print.succeed('Fetching starts...', opts)
|
|
11
6
|
const resp = await fetch(source.url, source.options ?? {})
|
|
12
|
-
if (isEmpty(resp))
|
|
7
|
+
if (isEmpty(resp)) spin.fatal('No result from server, aborted!')
|
|
13
8
|
if (source.abort) {
|
|
14
9
|
const aborted = await source.abort.call(this, resp)
|
|
15
|
-
if (aborted)
|
|
10
|
+
if (aborted) spin.fatal(aborted)
|
|
16
11
|
}
|
|
17
12
|
let count = 0
|
|
13
|
+
const stat = { created: 0, updated: 0, skipped: 0, error: 0 }
|
|
18
14
|
const iterator = isFunction(source.iterator) ? await source.iterator.call(this, resp) : resp[source.iterator]
|
|
19
|
-
|
|
15
|
+
if (iterator.length === 0) {
|
|
16
|
+
print.warn('No records to process, abort')
|
|
17
|
+
return 0
|
|
18
|
+
}
|
|
19
|
+
spin.setText('Got %d records, processing...', iterator.length)
|
|
20
20
|
for (let r of iterator) {
|
|
21
21
|
await setImmediate()
|
|
22
22
|
if (converter) r = await converter.call(this, r, options)
|
|
23
|
-
if (isEmpty(r))
|
|
23
|
+
if (isEmpty(r)) {
|
|
24
|
+
stat.skipped++
|
|
25
|
+
continue
|
|
26
|
+
}
|
|
24
27
|
try {
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
+
let existing
|
|
29
|
+
let record
|
|
30
|
+
let method
|
|
31
|
+
options.checkUnique = options.checkUnique ?? 'id'
|
|
32
|
+
if (['unique', 'upsert'].includes(options.mode)) {
|
|
33
|
+
const query = isFunction(options.checkUnique) ? await options.checkUnique.call(this, r, options) : set({}, options.checkUnique, r[options.checkUnique])
|
|
34
|
+
const resp = await recordFind(coll, { query, limit: 1 }, { skipCache: true })
|
|
35
|
+
if (resp.length > 0) existing = resp[0]
|
|
36
|
+
}
|
|
37
|
+
if (existing) {
|
|
38
|
+
if (options.mode === 'upsert') {
|
|
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
|
+
}
|
|
48
|
+
} else {
|
|
49
|
+
stat.created++
|
|
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 })
|
|
28
55
|
const recs = await recordFind(current.coll, { query }, { skipCache: true })
|
|
29
|
-
const rc = current.converter ? await current.converter.call(this, r, options) : r
|
|
56
|
+
const rc = current.converter ? await current.converter.call(this, { body: r, record, options }) : r
|
|
30
57
|
if (rc) {
|
|
31
58
|
if (recs.length > 0) {
|
|
32
59
|
const id = recs[0].id
|
|
@@ -36,17 +63,42 @@ async function fetchAndSave ({ source = {}, converter, coll, current = {}, optio
|
|
|
36
63
|
}
|
|
37
64
|
}
|
|
38
65
|
}
|
|
39
|
-
if (options.printCount && (count % options.printCount === 0)) print.succeed(
|
|
40
|
-
else
|
|
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)
|
|
41
68
|
count++
|
|
42
69
|
} catch (err) {
|
|
43
70
|
console.log(err)
|
|
44
|
-
|
|
71
|
+
spin.setText(validationErrorMessage(err) + ', continue')
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
print.succeed('[%s] %d/%d records processed', spin.getElapsed(), count, iterator.length)
|
|
75
|
+
print.succeed('[%s] Created: %d, Updated: %d, Skipped: %d', spin.getElapsed(), stat.created, stat.updated, stat.skipped)
|
|
76
|
+
return iterator.length
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
async function fetchAndSave ({ source = {}, converter, coll, current = {}, options = {} } = {}) {
|
|
80
|
+
const { print, spinner } = this.bajo.helper
|
|
81
|
+
source.options = source.options ?? {}
|
|
82
|
+
source.options.params = source.options.params ?? {}
|
|
83
|
+
if (options.batch) {
|
|
84
|
+
print.info('Batch starting')
|
|
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++
|
|
45
97
|
}
|
|
98
|
+
} else {
|
|
99
|
+
const spin = spinner({ showCounter: true }).start('Fetching starts...')
|
|
100
|
+
await batching.call(this, { source, converter, coll, current, options, spin })
|
|
46
101
|
}
|
|
47
|
-
spinner.info(`${count}/${iterator.length} records processed`)
|
|
48
|
-
if (options.returnEarly) print.info(`${count}/${iterator.length} records processed`, opts)
|
|
49
|
-
else spinner.succeed('Done!')
|
|
50
102
|
}
|
|
51
103
|
|
|
52
104
|
export default fetchAndSave
|
|
@@ -1,26 +1,21 @@
|
|
|
1
1
|
import Path from 'path'
|
|
2
2
|
|
|
3
|
-
function makeProgress (
|
|
3
|
+
function makeProgress (spin) {
|
|
4
4
|
return async function ({ batchNo, batchTotal, data } = {}) {
|
|
5
5
|
if (batchTotal === 0) return
|
|
6
|
-
|
|
6
|
+
spin.setText('Batch %d of %d (%d records)', batchNo, batchTotal, data.length)
|
|
7
7
|
}
|
|
8
8
|
}
|
|
9
9
|
|
|
10
|
-
async function exportTo ({ path, args
|
|
11
|
-
const { importPkg, print, dayjs, getConfig, importModule } = this.bajo.helper
|
|
10
|
+
async function exportTo ({ path, args }) {
|
|
11
|
+
const { importPkg, print, dayjs, getConfig, importModule, spinner } = this.bajo.helper
|
|
12
12
|
const { isEmpty, map } = await importPkg('lodash-es')
|
|
13
13
|
const [input, select] = await importPkg('bajo-cli:@inquirer/input',
|
|
14
14
|
'bajo-cli:@inquirer/select')
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
if (returnEarly) return
|
|
18
|
-
}
|
|
15
|
+
const config = getConfig()
|
|
16
|
+
if (!this.bajoDb) return print.fail('Bajo DB isn\'t loaded', { exit: config.tool })
|
|
19
17
|
const schemas = map(this.bajoDb.schemas, 'name')
|
|
20
|
-
if (isEmpty(schemas)) {
|
|
21
|
-
print.fail('No schema found!', { exit: !returnEarly })
|
|
22
|
-
if (returnEarly) return
|
|
23
|
-
}
|
|
18
|
+
if (isEmpty(schemas)) return print.fail('No schema found!', { exit: config.tool })
|
|
24
19
|
let [coll, dest, query] = args
|
|
25
20
|
if (isEmpty(coll)) {
|
|
26
21
|
coll = await select({
|
|
@@ -40,8 +35,8 @@ async function exportTo ({ path, args, returnEarly }) {
|
|
|
40
35
|
message: print.__('Please enter a query (if any):')
|
|
41
36
|
})
|
|
42
37
|
}
|
|
43
|
-
const
|
|
44
|
-
const progressFn = makeProgress.call(this,
|
|
38
|
+
const spin = spinner().start('Exporting...')
|
|
39
|
+
const progressFn = makeProgress.call(this, spin)
|
|
45
40
|
const cfg = getConfig('bajoDb', { full: true })
|
|
46
41
|
const { batch } = getConfig()
|
|
47
42
|
const start = await importModule(`${cfg.dir.pkg}/bajo/start.js`)
|
|
@@ -50,10 +45,10 @@ async function exportTo ({ path, args, returnEarly }) {
|
|
|
50
45
|
try {
|
|
51
46
|
const filter = { query }
|
|
52
47
|
const result = await this.bajoExtra.helper.exportTo(coll, dest, { filter, batch, progressFn })
|
|
53
|
-
|
|
48
|
+
spin.succeed('%d records successfully exported to \'%s\'', result.count, Path.resolve(result.file))
|
|
54
49
|
} catch (err) {
|
|
55
50
|
console.log(err)
|
|
56
|
-
|
|
51
|
+
spin.fail('Error: %s', err.message, { exit: config.tool })
|
|
57
52
|
}
|
|
58
53
|
}
|
|
59
54
|
|
|
@@ -1,25 +1,20 @@
|
|
|
1
1
|
import Path from 'path'
|
|
2
2
|
|
|
3
|
-
function makeProgress (
|
|
3
|
+
function makeProgress (spin) {
|
|
4
4
|
return async function ({ batchNo, data } = {}) {
|
|
5
|
-
|
|
5
|
+
spin.setText('Batch %d (%d records)', batchNo, data.length)
|
|
6
6
|
}
|
|
7
7
|
}
|
|
8
8
|
|
|
9
|
-
async function importFrom ({ path, args
|
|
10
|
-
const { importPkg, print, importModule, getConfig } = this.bajo.helper
|
|
9
|
+
async function importFrom ({ path, args }) {
|
|
10
|
+
const { importPkg, print, importModule, getConfig, spinner } = this.bajo.helper
|
|
11
11
|
const { isEmpty, map } = await importPkg('lodash-es')
|
|
12
12
|
const [input, select, confirm] = await importPkg('bajo-cli:@inquirer/input',
|
|
13
13
|
'bajo-cli:@inquirer/select', 'bajo-cli:@inquirer/confirm')
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
if (returnEarly) return
|
|
17
|
-
}
|
|
14
|
+
const config = getConfig()
|
|
15
|
+
if (!this.bajoDb) return print.fail('Bajo DB isn\'t loaded', { exit: config.tool })
|
|
18
16
|
const schemas = map(this.bajoDb.schemas, 'name')
|
|
19
|
-
if (isEmpty(schemas)) {
|
|
20
|
-
print.fail('No schema found!', { exit: !returnEarly })
|
|
21
|
-
if (returnEarly) return
|
|
22
|
-
}
|
|
17
|
+
if (isEmpty(schemas)) return print.fail('No schema found!', { exit: config.tool })
|
|
23
18
|
let [dest, coll] = args
|
|
24
19
|
if (isEmpty(dest)) {
|
|
25
20
|
dest = await input({
|
|
@@ -37,12 +32,9 @@ async function importFrom ({ path, args, returnEarly }) {
|
|
|
37
32
|
message: print.__('You\'re about to replace ALL records with the new ones. Are you really sure?'),
|
|
38
33
|
default: false
|
|
39
34
|
})
|
|
40
|
-
if (!answer) {
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
}
|
|
44
|
-
const spinner = print.bora('Importing...').start()
|
|
45
|
-
const progressFn = makeProgress.call(this, spinner)
|
|
35
|
+
if (!answer) return print.fail('Aborted!', { exit: config.tool })
|
|
36
|
+
const spin = spinner().start('Importing...')
|
|
37
|
+
const progressFn = makeProgress.call(this, spin)
|
|
46
38
|
const cfg = getConfig('bajoDb', { full: true })
|
|
47
39
|
const { batch } = getConfig()
|
|
48
40
|
const start = await importModule(`${cfg.dir.pkg}/bajo/start.js`)
|
|
@@ -50,9 +42,9 @@ async function importFrom ({ path, args, returnEarly }) {
|
|
|
50
42
|
await start.call(this, connection.name)
|
|
51
43
|
try {
|
|
52
44
|
const result = await this.bajoExtra.helper.importFrom(dest, coll, { batch, progressFn })
|
|
53
|
-
|
|
45
|
+
spin.succeed('%d records successfully imported from \'%s\'', result.count, Path.resolve(result.file))
|
|
54
46
|
} catch (err) {
|
|
55
|
-
|
|
47
|
+
spin.fail('Error: %s', err.message, { exit: config.tool })
|
|
56
48
|
}
|
|
57
49
|
}
|
|
58
50
|
|
package/bajoCli/tool.js
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
async function tool ({ path, args = []
|
|
1
|
+
async function tool ({ path, args = [] }) {
|
|
2
2
|
const { currentLoc } = this.bajo.helper
|
|
3
3
|
const { runToolMethod } = this.bajoCli.helper
|
|
4
4
|
const options = { demonize: ['shell'] }
|
|
5
|
-
await runToolMethod({ path, args, dir: `${currentLoc(import.meta).dir}/tool`, options
|
|
5
|
+
await runToolMethod({ path, args, dir: `${currentLoc(import.meta).dir}/tool`, options })
|
|
6
6
|
}
|
|
7
7
|
|
|
8
8
|
export default tool
|