dbgate-api 5.4.5-alpha.5 → 5.5.0
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/package.json +6 -5
- package/src/controllers/connections.js +1 -0
- package/src/controllers/databaseConnections.js +11 -0
- package/src/controllers/jsldata.js +6 -3
- package/src/currentVersion.js +2 -2
- package/src/proc/databaseConnectionProcess.js +43 -32
- package/src/shell/importDatabase.js +3 -1
- package/src/shell/index.js +4 -2
- package/src/shell/jsonLinesReader.js +4 -1
- package/src/shell/jsonReader.js +84 -0
- package/src/shell/jsonWriter.js +97 -0
- package/src/shell/modifyJsonLinesReader.js +1 -1
- package/src/shell/runScript.js +1 -1
- package/src/shell/jsonArrayWriter.js +0 -52
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "dbgate-api",
|
|
3
3
|
"main": "src/index.js",
|
|
4
|
-
"version": "5.
|
|
4
|
+
"version": "5.5.0",
|
|
5
5
|
"homepage": "https://dbgate.org/",
|
|
6
6
|
"repository": {
|
|
7
7
|
"type": "git",
|
|
@@ -26,10 +26,10 @@
|
|
|
26
26
|
"compare-versions": "^3.6.0",
|
|
27
27
|
"cors": "^2.8.5",
|
|
28
28
|
"cross-env": "^6.0.3",
|
|
29
|
-
"dbgate-datalib": "^5.
|
|
29
|
+
"dbgate-datalib": "^5.5.0",
|
|
30
30
|
"dbgate-query-splitter": "^4.10.3",
|
|
31
|
-
"dbgate-sqltree": "^5.
|
|
32
|
-
"dbgate-tools": "^5.
|
|
31
|
+
"dbgate-sqltree": "^5.5.0",
|
|
32
|
+
"dbgate-tools": "^5.5.0",
|
|
33
33
|
"debug": "^4.3.4",
|
|
34
34
|
"diff": "^5.0.0",
|
|
35
35
|
"diff2html": "^3.4.13",
|
|
@@ -57,6 +57,7 @@
|
|
|
57
57
|
"rimraf": "^3.0.0",
|
|
58
58
|
"simple-encryptor": "^4.0.0",
|
|
59
59
|
"ssh2": "^1.11.0",
|
|
60
|
+
"stream-json": "^1.8.0",
|
|
60
61
|
"tar": "^6.0.5"
|
|
61
62
|
},
|
|
62
63
|
"scripts": {
|
|
@@ -74,7 +75,7 @@
|
|
|
74
75
|
"devDependencies": {
|
|
75
76
|
"@types/fs-extra": "^9.0.11",
|
|
76
77
|
"@types/lodash": "^4.14.149",
|
|
77
|
-
"dbgate-types": "^5.
|
|
78
|
+
"dbgate-types": "^5.5.0",
|
|
78
79
|
"env-cmd": "^10.1.0",
|
|
79
80
|
"node-loader": "^1.0.2",
|
|
80
81
|
"nodemon": "^2.0.2",
|
|
@@ -76,6 +76,7 @@ function getPortalCollections() {
|
|
|
76
76
|
allowedDatabases: process.env[`ALLOWED_DATABASES_${id}`]?.replace(/\|/g, '\n'),
|
|
77
77
|
allowedDatabasesRegex: process.env[`ALLOWED_DATABASES_REGEX_${id}`],
|
|
78
78
|
parent: process.env[`PARENT_${id}`] || undefined,
|
|
79
|
+
useSeparateSchemas: !!process.env[`USE_SEPARATE_SCHEMAS_${id}`],
|
|
79
80
|
|
|
80
81
|
// SSH tunnel
|
|
81
82
|
useSshTunnel: process.env[`USE_SSH_${id}`],
|
|
@@ -213,6 +213,17 @@ module.exports = {
|
|
|
213
213
|
return res.result || null;
|
|
214
214
|
},
|
|
215
215
|
|
|
216
|
+
schemaList_meta: true,
|
|
217
|
+
async schemaList({ conid, database }, req) {
|
|
218
|
+
testConnectionPermission(conid, req);
|
|
219
|
+
return this.loadDataCore('schemaList', { conid, database });
|
|
220
|
+
},
|
|
221
|
+
|
|
222
|
+
dispatchDatabaseChangedEvent_meta: true,
|
|
223
|
+
dispatchDatabaseChangedEvent({ event, conid, database }) {
|
|
224
|
+
socket.emitChanged(event, { conid, database });
|
|
225
|
+
},
|
|
226
|
+
|
|
216
227
|
loadKeys_meta: true,
|
|
217
228
|
async loadKeys({ conid, database, root, filter }, req) {
|
|
218
229
|
testConnectionPermission(conid, req);
|
|
@@ -18,11 +18,14 @@ function readFirstLine(file) {
|
|
|
18
18
|
}
|
|
19
19
|
if (reader.hasNextLine()) {
|
|
20
20
|
reader.nextLine((err, line) => {
|
|
21
|
-
if (err)
|
|
22
|
-
|
|
21
|
+
if (err) {
|
|
22
|
+
reader.close(() => reject(err)); // Ensure reader is closed on error
|
|
23
|
+
return;
|
|
24
|
+
}
|
|
25
|
+
reader.close(() => resolve(line)); // Ensure reader is closed after reading
|
|
23
26
|
});
|
|
24
27
|
} else {
|
|
25
|
-
resolve(null);
|
|
28
|
+
reader.close(() => resolve(null)); // Properly close if no lines are present
|
|
26
29
|
}
|
|
27
30
|
});
|
|
28
31
|
});
|
package/src/currentVersion.js
CHANGED
|
@@ -11,7 +11,7 @@ const { dumpSqlSelect } = require('dbgate-sqltree');
|
|
|
11
11
|
|
|
12
12
|
const logger = getLogger('dbconnProcess');
|
|
13
13
|
|
|
14
|
-
let
|
|
14
|
+
let dbhan;
|
|
15
15
|
let storedConnection;
|
|
16
16
|
let afterConnectCallbacks = [];
|
|
17
17
|
let afterAnalyseCallbacks = [];
|
|
@@ -49,7 +49,7 @@ async function handleFullRefresh() {
|
|
|
49
49
|
loadingModel = true;
|
|
50
50
|
const driver = requireEngineDriver(storedConnection);
|
|
51
51
|
setStatusName('loadStructure');
|
|
52
|
-
analysedStructure = await checkedAsyncCall(driver.analyseFull(
|
|
52
|
+
analysedStructure = await checkedAsyncCall(driver.analyseFull(dbhan, serverVersion));
|
|
53
53
|
analysedTime = new Date().getTime();
|
|
54
54
|
process.send({ msgtype: 'structure', structure: analysedStructure });
|
|
55
55
|
process.send({ msgtype: 'structureTime', analysedTime });
|
|
@@ -63,9 +63,7 @@ async function handleIncrementalRefresh(forceSend) {
|
|
|
63
63
|
loadingModel = true;
|
|
64
64
|
const driver = requireEngineDriver(storedConnection);
|
|
65
65
|
setStatusName('checkStructure');
|
|
66
|
-
const newStructure = await checkedAsyncCall(
|
|
67
|
-
driver.analyseIncremental(systemConnection, analysedStructure, serverVersion)
|
|
68
|
-
);
|
|
66
|
+
const newStructure = await checkedAsyncCall(driver.analyseIncremental(dbhan, analysedStructure, serverVersion));
|
|
69
67
|
analysedTime = new Date().getTime();
|
|
70
68
|
if (newStructure != null) {
|
|
71
69
|
analysedStructure = newStructure;
|
|
@@ -103,7 +101,7 @@ function setStatusName(name) {
|
|
|
103
101
|
|
|
104
102
|
async function readVersion() {
|
|
105
103
|
const driver = requireEngineDriver(storedConnection);
|
|
106
|
-
const version = await driver.getVersion(
|
|
104
|
+
const version = await driver.getVersion(dbhan);
|
|
107
105
|
process.send({ msgtype: 'version', version });
|
|
108
106
|
serverVersion = version;
|
|
109
107
|
}
|
|
@@ -114,8 +112,8 @@ async function handleConnect({ connection, structure, globalSettings }) {
|
|
|
114
112
|
|
|
115
113
|
if (!structure) setStatusName('pending');
|
|
116
114
|
const driver = requireEngineDriver(storedConnection);
|
|
117
|
-
|
|
118
|
-
|
|
115
|
+
dbhan = await checkedAsyncCall(connectUtility(driver, storedConnection, 'app'));
|
|
116
|
+
dbhan.feedback = feedback => setStatus({ feedback });
|
|
119
117
|
await checkedAsyncCall(readVersion());
|
|
120
118
|
if (structure) {
|
|
121
119
|
analysedStructure = structure;
|
|
@@ -138,7 +136,7 @@ async function handleConnect({ connection, structure, globalSettings }) {
|
|
|
138
136
|
}
|
|
139
137
|
|
|
140
138
|
function waitConnected() {
|
|
141
|
-
if (
|
|
139
|
+
if (dbhan) return Promise.resolve();
|
|
142
140
|
return new Promise((resolve, reject) => {
|
|
143
141
|
afterConnectCallbacks.push([resolve, reject]);
|
|
144
142
|
});
|
|
@@ -163,7 +161,7 @@ async function handleRunScript({ msgid, sql, useTransaction }, skipReadonlyCheck
|
|
|
163
161
|
const driver = requireEngineDriver(storedConnection);
|
|
164
162
|
try {
|
|
165
163
|
if (!skipReadonlyCheck) ensureExecuteCustomScript(driver);
|
|
166
|
-
await driver.script(
|
|
164
|
+
await driver.script(dbhan, sql, { useTransaction });
|
|
167
165
|
process.send({ msgtype: 'response', msgid });
|
|
168
166
|
} catch (err) {
|
|
169
167
|
process.send({ msgtype: 'response', msgid, errorMessage: err.message });
|
|
@@ -175,7 +173,7 @@ async function handleRunOperation({ msgid, operation, useTransaction }, skipRead
|
|
|
175
173
|
const driver = requireEngineDriver(storedConnection);
|
|
176
174
|
try {
|
|
177
175
|
if (!skipReadonlyCheck) ensureExecuteCustomScript(driver);
|
|
178
|
-
await driver.operation(
|
|
176
|
+
await driver.operation(dbhan, operation, { useTransaction });
|
|
179
177
|
process.send({ msgtype: 'response', msgid });
|
|
180
178
|
} catch (err) {
|
|
181
179
|
process.send({ msgtype: 'response', msgid, errorMessage: err.message });
|
|
@@ -188,7 +186,7 @@ async function handleQueryData({ msgid, sql }, skipReadonlyCheck = false) {
|
|
|
188
186
|
try {
|
|
189
187
|
if (!skipReadonlyCheck) ensureExecuteCustomScript(driver);
|
|
190
188
|
// console.log(sql);
|
|
191
|
-
const res = await driver.query(
|
|
189
|
+
const res = await driver.query(dbhan, sql);
|
|
192
190
|
process.send({ msgtype: 'response', msgid, ...res });
|
|
193
191
|
} catch (err) {
|
|
194
192
|
process.send({ msgtype: 'response', msgid, errorMessage: err.message || 'Error executing SQL script' });
|
|
@@ -202,52 +200,64 @@ async function handleSqlSelect({ msgid, select }) {
|
|
|
202
200
|
return handleQueryData({ msgid, sql: dmp.s }, true);
|
|
203
201
|
}
|
|
204
202
|
|
|
205
|
-
async function handleDriverDataCore(msgid, callMethod) {
|
|
203
|
+
async function handleDriverDataCore(msgid, callMethod, { logName }) {
|
|
206
204
|
await waitConnected();
|
|
207
205
|
const driver = requireEngineDriver(storedConnection);
|
|
208
206
|
try {
|
|
209
207
|
const result = await callMethod(driver);
|
|
210
208
|
process.send({ msgtype: 'response', msgid, result });
|
|
211
209
|
} catch (err) {
|
|
210
|
+
logger.error(err, `Error when handling message ${logName}`);
|
|
212
211
|
process.send({ msgtype: 'response', msgid, errorMessage: err.message });
|
|
213
212
|
}
|
|
214
213
|
}
|
|
215
214
|
|
|
215
|
+
async function handleSchemaList({ msgid }) {
|
|
216
|
+
logger.debug('Loading schema list');
|
|
217
|
+
return handleDriverDataCore(msgid, driver => driver.listSchemas(dbhan), { logName: 'listSchemas' });
|
|
218
|
+
}
|
|
219
|
+
|
|
216
220
|
async function handleCollectionData({ msgid, options }) {
|
|
217
|
-
return handleDriverDataCore(msgid, driver => driver.readCollection(
|
|
221
|
+
return handleDriverDataCore(msgid, driver => driver.readCollection(dbhan, options), { logName: 'readCollection' });
|
|
218
222
|
}
|
|
219
223
|
|
|
220
224
|
async function handleLoadKeys({ msgid, root, filter }) {
|
|
221
|
-
return handleDriverDataCore(msgid, driver => driver.loadKeys(
|
|
225
|
+
return handleDriverDataCore(msgid, driver => driver.loadKeys(dbhan, root, filter), { logName: 'loadKeys' });
|
|
222
226
|
}
|
|
223
227
|
|
|
224
228
|
async function handleExportKeys({ msgid, options }) {
|
|
225
|
-
return handleDriverDataCore(msgid, driver => driver.exportKeys(
|
|
229
|
+
return handleDriverDataCore(msgid, driver => driver.exportKeys(dbhan, options), { logName: 'exportKeys' });
|
|
226
230
|
}
|
|
227
231
|
|
|
228
232
|
async function handleLoadKeyInfo({ msgid, key }) {
|
|
229
|
-
return handleDriverDataCore(msgid, driver => driver.loadKeyInfo(
|
|
233
|
+
return handleDriverDataCore(msgid, driver => driver.loadKeyInfo(dbhan, key), { logName: 'loadKeyInfo' });
|
|
230
234
|
}
|
|
231
235
|
|
|
232
236
|
async function handleCallMethod({ msgid, method, args }) {
|
|
233
|
-
return handleDriverDataCore(
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
237
|
+
return handleDriverDataCore(
|
|
238
|
+
msgid,
|
|
239
|
+
driver => {
|
|
240
|
+
if (storedConnection.isReadOnly) {
|
|
241
|
+
throw new Error('Connection is read only, cannot call custom methods');
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
ensureExecuteCustomScript(driver);
|
|
245
|
+
return driver.callMethod(dbhan, method, args);
|
|
246
|
+
},
|
|
247
|
+
{ logName: `callMethod:${method}` }
|
|
248
|
+
);
|
|
241
249
|
}
|
|
242
250
|
|
|
243
251
|
async function handleLoadKeyTableRange({ msgid, key, cursor, count }) {
|
|
244
|
-
return handleDriverDataCore(msgid, driver => driver.loadKeyTableRange(
|
|
252
|
+
return handleDriverDataCore(msgid, driver => driver.loadKeyTableRange(dbhan, key, cursor, count), {
|
|
253
|
+
logName: 'loadKeyTableRange',
|
|
254
|
+
});
|
|
245
255
|
}
|
|
246
256
|
|
|
247
257
|
async function handleLoadFieldValues({ msgid, schemaName, pureName, field, search }) {
|
|
248
|
-
return handleDriverDataCore(msgid, driver =>
|
|
249
|
-
|
|
250
|
-
);
|
|
258
|
+
return handleDriverDataCore(msgid, driver => driver.loadFieldValues(dbhan, { schemaName, pureName }, field, search), {
|
|
259
|
+
logName: 'loadFieldValues',
|
|
260
|
+
});
|
|
251
261
|
}
|
|
252
262
|
|
|
253
263
|
function ensureExecuteCustomScript(driver) {
|
|
@@ -264,7 +274,7 @@ async function handleUpdateCollection({ msgid, changeSet }) {
|
|
|
264
274
|
const driver = requireEngineDriver(storedConnection);
|
|
265
275
|
try {
|
|
266
276
|
ensureExecuteCustomScript(driver);
|
|
267
|
-
const result = await driver.updateCollection(
|
|
277
|
+
const result = await driver.updateCollection(dbhan, changeSet);
|
|
268
278
|
process.send({ msgtype: 'response', msgid, result });
|
|
269
279
|
} catch (err) {
|
|
270
280
|
process.send({ msgtype: 'response', msgid, errorMessage: err.message });
|
|
@@ -277,7 +287,7 @@ async function handleSqlPreview({ msgid, objects, options }) {
|
|
|
277
287
|
|
|
278
288
|
try {
|
|
279
289
|
const dmp = driver.createDumper();
|
|
280
|
-
const generator = new SqlGenerator(analysedStructure, options, objects, dmp, driver,
|
|
290
|
+
const generator = new SqlGenerator(analysedStructure, options, objects, dmp, driver, dbhan);
|
|
281
291
|
|
|
282
292
|
await generator.dump();
|
|
283
293
|
process.send({ msgtype: 'response', msgid, sql: dmp.s, isTruncated: generator.isTruncated });
|
|
@@ -297,7 +307,7 @@ async function handleGenerateDeploySql({ msgid, modelFolder }) {
|
|
|
297
307
|
|
|
298
308
|
try {
|
|
299
309
|
const res = await generateDeploySql({
|
|
300
|
-
systemConnection,
|
|
310
|
+
systemConnection: dbhan,
|
|
301
311
|
connection: storedConnection,
|
|
302
312
|
analysedStructure,
|
|
303
313
|
modelFolder,
|
|
@@ -337,6 +347,7 @@ const messageHandlers = {
|
|
|
337
347
|
loadFieldValues: handleLoadFieldValues,
|
|
338
348
|
sqlSelect: handleSqlSelect,
|
|
339
349
|
exportKeys: handleExportKeys,
|
|
350
|
+
schemaList: handleSchemaList,
|
|
340
351
|
// runCommand: handleRunCommand,
|
|
341
352
|
};
|
|
342
353
|
|
|
@@ -16,7 +16,7 @@ class ImportStream extends stream.Transform {
|
|
|
16
16
|
}
|
|
17
17
|
async _transform(chunk, encoding, cb) {
|
|
18
18
|
try {
|
|
19
|
-
await this.driver.script(this.pool, chunk);
|
|
19
|
+
await this.driver.script(this.pool, chunk, { queryOptions: { importSqlDump: true } });
|
|
20
20
|
} catch (err) {
|
|
21
21
|
this.emit('error', err.message);
|
|
22
22
|
}
|
|
@@ -47,7 +47,9 @@ async function importDatabase({ connection = undefined, systemConnection = undef
|
|
|
47
47
|
const pool = systemConnection || (await connectUtility(driver, connection, 'write'));
|
|
48
48
|
logger.info(`Connected.`);
|
|
49
49
|
|
|
50
|
+
logger.info(`Input file: ${inputFile}`);
|
|
50
51
|
const downloadedFile = await download(inputFile);
|
|
52
|
+
logger.info(`Downloaded file: ${downloadedFile}`);
|
|
51
53
|
|
|
52
54
|
const fileStream = fs.createReadStream(downloadedFile, 'utf-8');
|
|
53
55
|
const splittedStream = splitQueryStream(fileStream, driver.getQuerySplitterOptions('script'));
|
package/src/shell/index.js
CHANGED
|
@@ -6,7 +6,7 @@ const copyStream = require('./copyStream');
|
|
|
6
6
|
const fakeObjectReader = require('./fakeObjectReader');
|
|
7
7
|
const consoleObjectWriter = require('./consoleObjectWriter');
|
|
8
8
|
const jsonLinesWriter = require('./jsonLinesWriter');
|
|
9
|
-
const
|
|
9
|
+
const jsonWriter = require('./jsonWriter');
|
|
10
10
|
const jsonLinesReader = require('./jsonLinesReader');
|
|
11
11
|
const sqlDataWriter = require('./sqlDataWriter');
|
|
12
12
|
const jslDataReader = require('./jslDataReader');
|
|
@@ -29,6 +29,7 @@ const modifyJsonLinesReader = require('./modifyJsonLinesReader');
|
|
|
29
29
|
const dataDuplicator = require('./dataDuplicator');
|
|
30
30
|
const dbModelToJson = require('./dbModelToJson');
|
|
31
31
|
const jsonToDbModel = require('./jsonToDbModel');
|
|
32
|
+
const jsonReader = require('./jsonReader');
|
|
32
33
|
|
|
33
34
|
const dbgateApi = {
|
|
34
35
|
queryReader,
|
|
@@ -37,8 +38,9 @@ const dbgateApi = {
|
|
|
37
38
|
tableReader,
|
|
38
39
|
copyStream,
|
|
39
40
|
jsonLinesWriter,
|
|
40
|
-
jsonArrayWriter,
|
|
41
41
|
jsonLinesReader,
|
|
42
|
+
jsonReader,
|
|
43
|
+
jsonWriter,
|
|
42
44
|
sqlDataWriter,
|
|
43
45
|
fakeObjectReader,
|
|
44
46
|
consoleObjectWriter,
|
|
@@ -2,6 +2,7 @@ const fs = require('fs');
|
|
|
2
2
|
const stream = require('stream');
|
|
3
3
|
const byline = require('byline');
|
|
4
4
|
const { getLogger } = require('dbgate-tools');
|
|
5
|
+
const download = require('./download');
|
|
5
6
|
const logger = getLogger('jsonLinesReader');
|
|
6
7
|
|
|
7
8
|
class ParseStream extends stream.Transform {
|
|
@@ -35,8 +36,10 @@ class ParseStream extends stream.Transform {
|
|
|
35
36
|
async function jsonLinesReader({ fileName, encoding = 'utf-8', limitRows = undefined }) {
|
|
36
37
|
logger.info(`Reading file ${fileName}`);
|
|
37
38
|
|
|
39
|
+
const downloadedFile = await download(fileName);
|
|
40
|
+
|
|
38
41
|
const fileStream = fs.createReadStream(
|
|
39
|
-
|
|
42
|
+
downloadedFile,
|
|
40
43
|
// @ts-ignore
|
|
41
44
|
encoding
|
|
42
45
|
);
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
const fs = require('fs');
|
|
2
|
+
const stream = require('stream');
|
|
3
|
+
const byline = require('byline');
|
|
4
|
+
const { getLogger } = require('dbgate-tools');
|
|
5
|
+
const { parser } = require('stream-json');
|
|
6
|
+
const { pick } = require('stream-json/filters/Pick');
|
|
7
|
+
const { streamArray } = require('stream-json/streamers/StreamArray');
|
|
8
|
+
const { streamObject } = require('stream-json/streamers/StreamObject');
|
|
9
|
+
const download = require('./download');
|
|
10
|
+
|
|
11
|
+
const logger = getLogger('jsonReader');
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
class ParseStream extends stream.Transform {
|
|
15
|
+
constructor({ limitRows, jsonStyle, keyField }) {
|
|
16
|
+
super({ objectMode: true });
|
|
17
|
+
this.wasHeader = false;
|
|
18
|
+
this.limitRows = limitRows;
|
|
19
|
+
this.jsonStyle = jsonStyle;
|
|
20
|
+
this.keyField = keyField || '_key';
|
|
21
|
+
this.rowsWritten = 0;
|
|
22
|
+
}
|
|
23
|
+
_transform(chunk, encoding, done) {
|
|
24
|
+
if (!this.wasHeader) {
|
|
25
|
+
this.push({
|
|
26
|
+
__isStreamHeader: true,
|
|
27
|
+
__isDynamicStructure: true,
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
this.wasHeader = true;
|
|
31
|
+
}
|
|
32
|
+
if (!this.limitRows || this.rowsWritten < this.limitRows) {
|
|
33
|
+
if (this.jsonStyle === 'object') {
|
|
34
|
+
this.push({
|
|
35
|
+
...chunk.value,
|
|
36
|
+
[this.keyField]: chunk.key,
|
|
37
|
+
});
|
|
38
|
+
} else {
|
|
39
|
+
this.push(chunk.value);
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
this.rowsWritten += 1;
|
|
43
|
+
}
|
|
44
|
+
done();
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
async function jsonReader({
|
|
49
|
+
fileName,
|
|
50
|
+
jsonStyle,
|
|
51
|
+
keyField = '_key',
|
|
52
|
+
rootField = null,
|
|
53
|
+
encoding = 'utf-8',
|
|
54
|
+
limitRows = undefined,
|
|
55
|
+
}) {
|
|
56
|
+
logger.info(`Reading file ${fileName}`);
|
|
57
|
+
|
|
58
|
+
const downloadedFile = await download(fileName);
|
|
59
|
+
const fileStream = fs.createReadStream(
|
|
60
|
+
downloadedFile,
|
|
61
|
+
// @ts-ignore
|
|
62
|
+
encoding
|
|
63
|
+
);
|
|
64
|
+
const parseJsonStream = parser();
|
|
65
|
+
fileStream.pipe(parseJsonStream);
|
|
66
|
+
|
|
67
|
+
const parseStream = new ParseStream({ limitRows, jsonStyle, keyField });
|
|
68
|
+
|
|
69
|
+
const tramsformer = jsonStyle === 'object' ? streamObject() : streamArray();
|
|
70
|
+
|
|
71
|
+
if (rootField) {
|
|
72
|
+
const filterStream = pick({ filter: rootField });
|
|
73
|
+
parseJsonStream.pipe(filterStream);
|
|
74
|
+
filterStream.pipe(tramsformer);
|
|
75
|
+
} else {
|
|
76
|
+
parseJsonStream.pipe(tramsformer);
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
tramsformer.pipe(parseStream);
|
|
80
|
+
|
|
81
|
+
return parseStream;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
module.exports = jsonReader;
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
const { getLogger } = require('dbgate-tools');
|
|
2
|
+
const fs = require('fs');
|
|
3
|
+
const stream = require('stream');
|
|
4
|
+
const _ = require('lodash');
|
|
5
|
+
|
|
6
|
+
const logger = getLogger('jsonArrayWriter');
|
|
7
|
+
|
|
8
|
+
class StringifyStream extends stream.Transform {
|
|
9
|
+
constructor({ jsonStyle, keyField, rootField }) {
|
|
10
|
+
super({ objectMode: true });
|
|
11
|
+
this.wasHeader = false;
|
|
12
|
+
this.wasRecord = false;
|
|
13
|
+
this.jsonStyle = jsonStyle;
|
|
14
|
+
this.keyField = keyField || '_key';
|
|
15
|
+
this.rootField = rootField;
|
|
16
|
+
}
|
|
17
|
+
_transform(chunk, encoding, done) {
|
|
18
|
+
let skip = false;
|
|
19
|
+
|
|
20
|
+
if (!this.wasHeader) {
|
|
21
|
+
skip = chunk.__isStreamHeader;
|
|
22
|
+
this.wasHeader = true;
|
|
23
|
+
}
|
|
24
|
+
if (!skip) {
|
|
25
|
+
if (!this.wasRecord) {
|
|
26
|
+
if (this.rootField) {
|
|
27
|
+
if (this.jsonStyle === 'object') {
|
|
28
|
+
this.push(`{"${this.rootField}": {\n`);
|
|
29
|
+
} else {
|
|
30
|
+
this.push(`{"${this.rootField}": [\n`);
|
|
31
|
+
}
|
|
32
|
+
} else {
|
|
33
|
+
if (this.jsonStyle === 'object') {
|
|
34
|
+
this.push('{\n');
|
|
35
|
+
} else {
|
|
36
|
+
this.push('[\n');
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
} else {
|
|
40
|
+
this.push(',\n');
|
|
41
|
+
}
|
|
42
|
+
this.wasRecord = true;
|
|
43
|
+
|
|
44
|
+
if (this.jsonStyle === 'object') {
|
|
45
|
+
const key = chunk[this.keyField] ?? chunk[Object.keys(chunk)[0]];
|
|
46
|
+
this.push(`"${key}": ${JSON.stringify(_.omit(chunk, [this.keyField]))}`);
|
|
47
|
+
} else {
|
|
48
|
+
this.push(JSON.stringify(chunk));
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
done();
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
_flush(done) {
|
|
55
|
+
if (!this.wasRecord) {
|
|
56
|
+
if (this.rootField) {
|
|
57
|
+
if (this.jsonStyle === 'object') {
|
|
58
|
+
this.push(`{"${this.rootField}": {}}\n`);
|
|
59
|
+
} else {
|
|
60
|
+
this.push(`{"${this.rootField}": []}\n`);
|
|
61
|
+
}
|
|
62
|
+
} else {
|
|
63
|
+
if (this.jsonStyle === 'object') {
|
|
64
|
+
this.push('{}\n');
|
|
65
|
+
} else {
|
|
66
|
+
this.push('[]\n');
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
} else {
|
|
70
|
+
if (this.rootField) {
|
|
71
|
+
if (this.jsonStyle === 'object') {
|
|
72
|
+
this.push('\n}}\n');
|
|
73
|
+
} else {
|
|
74
|
+
this.push('\n]}\n');
|
|
75
|
+
}
|
|
76
|
+
} else {
|
|
77
|
+
if (this.jsonStyle === 'object') {
|
|
78
|
+
this.push('\n}\n');
|
|
79
|
+
} else {
|
|
80
|
+
this.push('\n]\n');
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
done();
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
async function jsonWriter({ fileName, jsonStyle, keyField = '_key', rootField, encoding = 'utf-8' }) {
|
|
89
|
+
logger.info(`Writing file ${fileName}`);
|
|
90
|
+
const stringify = new StringifyStream({ jsonStyle, keyField, rootField });
|
|
91
|
+
const fileStream = fs.createWriteStream(fileName, encoding);
|
|
92
|
+
stringify.pipe(fileStream);
|
|
93
|
+
stringify['finisher'] = fileStream;
|
|
94
|
+
return stringify;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
module.exports = jsonWriter;
|
package/src/shell/runScript.js
CHANGED
|
@@ -1,52 +0,0 @@
|
|
|
1
|
-
const { getLogger } = require('dbgate-tools');
|
|
2
|
-
const fs = require('fs');
|
|
3
|
-
const stream = require('stream');
|
|
4
|
-
|
|
5
|
-
const logger = getLogger('jsonArrayWriter');
|
|
6
|
-
|
|
7
|
-
class StringifyStream extends stream.Transform {
|
|
8
|
-
constructor() {
|
|
9
|
-
super({ objectMode: true });
|
|
10
|
-
this.wasHeader = false;
|
|
11
|
-
this.wasRecord = false;
|
|
12
|
-
}
|
|
13
|
-
_transform(chunk, encoding, done) {
|
|
14
|
-
let skip = false;
|
|
15
|
-
|
|
16
|
-
if (!this.wasHeader) {
|
|
17
|
-
skip = chunk.__isStreamHeader;
|
|
18
|
-
this.wasHeader = true;
|
|
19
|
-
}
|
|
20
|
-
if (!skip) {
|
|
21
|
-
if (!this.wasRecord) {
|
|
22
|
-
this.push('[\n');
|
|
23
|
-
} else {
|
|
24
|
-
this.push(',\n');
|
|
25
|
-
}
|
|
26
|
-
this.wasRecord = true;
|
|
27
|
-
|
|
28
|
-
this.push(JSON.stringify(chunk));
|
|
29
|
-
}
|
|
30
|
-
done();
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
_flush(done) {
|
|
34
|
-
if (!this.wasRecord) {
|
|
35
|
-
this.push('[]\n');
|
|
36
|
-
} else {
|
|
37
|
-
this.push('\n]\n');
|
|
38
|
-
}
|
|
39
|
-
done();
|
|
40
|
-
}
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
async function jsonArrayWriter({ fileName, encoding = 'utf-8' }) {
|
|
44
|
-
logger.info(`Writing file ${fileName}`);
|
|
45
|
-
const stringify = new StringifyStream();
|
|
46
|
-
const fileStream = fs.createWriteStream(fileName, encoding);
|
|
47
|
-
stringify.pipe(fileStream);
|
|
48
|
-
stringify['finisher'] = fileStream;
|
|
49
|
-
return stringify;
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
module.exports = jsonArrayWriter;
|