mirror of
https://github.com/DeNNiiInc/dbgate.git
synced 2026-05-01 02:43:59 +00:00
Merge branch 'feature/import-export'
This commit is contained in:
1
.github/workflows/run-tests.yaml
vendored
1
.github/workflows/run-tests.yaml
vendored
@@ -4,6 +4,7 @@ on:
|
|||||||
branches:
|
branches:
|
||||||
- master
|
- master
|
||||||
- develop
|
- develop
|
||||||
|
- 'feature/**'
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
test-runner:
|
test-runner:
|
||||||
|
|||||||
151
integration-tests/__tests__/import-formats.spec.js
Normal file
151
integration-tests/__tests__/import-formats.spec.js
Normal file
@@ -0,0 +1,151 @@
|
|||||||
|
const dbgateApi = require('dbgate-api/src/shell');
|
||||||
|
// const jsonLinesWriter = require('dbgate-api/src/shell/jsonLinesWriter');
|
||||||
|
const tmp = require('tmp');
|
||||||
|
// const dbgatePluginCsv = require('dbgate-plugin-csv/src/backend');
|
||||||
|
const fs = require('fs');
|
||||||
|
const requirePlugin = require('dbgate-api/src/shell/requirePlugin');
|
||||||
|
|
||||||
|
const CSV_DATA = `Issue Number; Title; Github URL; Labels; State; Created At; Updated At; Reporter; Assignee
|
||||||
|
801; "Does it 'burst' the database on startup or first lUI load ? "; https://github.com/dbgate/dbgate/issues/801; ""; open; 05/23/2024; 05/23/2024; rgarrigue;
|
||||||
|
799; "BUG: latest AppImage crashes on opening in Fedora 39"; https://github.com/dbgate/dbgate/issues/799; ""; open; 05/21/2024; 05/24/2024; BenGraham-Git;
|
||||||
|
798; "MongoDB write operations fail"; https://github.com/dbgate/dbgate/issues/798; "bug,solved"; open; 05/21/2024; 05/24/2024; mahmed0715;
|
||||||
|
797; "BUG: Unable to open SQL files"; https://github.com/dbgate/dbgate/issues/797; "bug"; open; 05/20/2024; 05/21/2024; cesarValdivia;
|
||||||
|
795; "BUG: MS SQL Server connection error (KEY_USAGE_BIT_INCORRECT)"; https://github.com/dbgate/dbgate/issues/795; ""; open; 05/20/2024; 05/20/2024; keskinonur;
|
||||||
|
794; "GLIBC_2.29' not found and i have 2.31"; https://github.com/dbgate/dbgate/issues/794; ""; closed; 05/20/2024; 05/21/2024; MFdanGM;
|
||||||
|
793; "BUG: PostgresSQL doesn't show tables when connected"; https://github.com/dbgate/dbgate/issues/793; ""; open; 05/20/2024; 05/22/2024; stomper013;
|
||||||
|
792; "FEAT: Wayland support"; https://github.com/dbgate/dbgate/issues/792; ""; closed; 05/19/2024; 05/21/2024; VosaXalo;
|
||||||
|
`;
|
||||||
|
|
||||||
|
async function getReaderRows(reader) {
|
||||||
|
const jsonLinesFileName = tmp.tmpNameSync();
|
||||||
|
|
||||||
|
const writer = await dbgateApi.jsonLinesWriter({
|
||||||
|
fileName: jsonLinesFileName,
|
||||||
|
});
|
||||||
|
await dbgateApi.copyStream(reader, writer);
|
||||||
|
|
||||||
|
const jsonData = fs.readFileSync(jsonLinesFileName, 'utf-8');
|
||||||
|
const rows = jsonData
|
||||||
|
.split('\n')
|
||||||
|
.filter(x => x.trim() !== '')
|
||||||
|
.map(x => JSON.parse(x));
|
||||||
|
|
||||||
|
return rows;
|
||||||
|
}
|
||||||
|
|
||||||
|
test('csv import test', async () => {
|
||||||
|
const dbgatePluginCsv = requirePlugin('dbgate-plugin-csv');
|
||||||
|
|
||||||
|
const csvFileName = tmp.tmpNameSync();
|
||||||
|
|
||||||
|
fs.writeFileSync(csvFileName, CSV_DATA);
|
||||||
|
|
||||||
|
const reader = await dbgatePluginCsv.shellApi.reader({
|
||||||
|
fileName: csvFileName,
|
||||||
|
});
|
||||||
|
|
||||||
|
const rows = await getReaderRows(reader);
|
||||||
|
|
||||||
|
expect(rows[0].columns).toEqual([
|
||||||
|
{ columnName: 'Issue Number' },
|
||||||
|
{ columnName: 'Title' },
|
||||||
|
{ columnName: 'Github URL' },
|
||||||
|
{ columnName: 'Labels' },
|
||||||
|
{ columnName: 'State' },
|
||||||
|
{ columnName: 'Created At' },
|
||||||
|
{ columnName: 'Updated At' },
|
||||||
|
{ columnName: 'Reporter' },
|
||||||
|
{ columnName: 'Assignee' },
|
||||||
|
]);
|
||||||
|
expect(rows.length).toEqual(9);
|
||||||
|
expect(rows[1]).toEqual({
|
||||||
|
'Issue Number': '801',
|
||||||
|
Title: "Does it 'burst' the database on startup or first lUI load ? ",
|
||||||
|
'Github URL': 'https://github.com/dbgate/dbgate/issues/801',
|
||||||
|
Labels: '',
|
||||||
|
State: 'open',
|
||||||
|
'Created At': '05/23/2024',
|
||||||
|
'Updated At': '05/23/2024',
|
||||||
|
Reporter: 'rgarrigue',
|
||||||
|
Assignee: '',
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
test('JSON array import test', async () => {
|
||||||
|
const jsonFileName = tmp.tmpNameSync();
|
||||||
|
|
||||||
|
fs.writeFileSync(
|
||||||
|
jsonFileName,
|
||||||
|
JSON.stringify([
|
||||||
|
{ id: 1, val: 'v1' },
|
||||||
|
{ id: 2, val: 'v2' },
|
||||||
|
])
|
||||||
|
);
|
||||||
|
|
||||||
|
const reader = await dbgateApi.jsonReader({
|
||||||
|
fileName: jsonFileName,
|
||||||
|
});
|
||||||
|
|
||||||
|
const rows = await getReaderRows(reader);
|
||||||
|
|
||||||
|
expect(rows.length).toEqual(2);
|
||||||
|
expect(rows).toEqual([
|
||||||
|
{ id: 1, val: 'v1' },
|
||||||
|
{ id: 2, val: 'v2' },
|
||||||
|
]);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('JSON object import test', async () => {
|
||||||
|
const jsonFileName = tmp.tmpNameSync();
|
||||||
|
|
||||||
|
fs.writeFileSync(
|
||||||
|
jsonFileName,
|
||||||
|
JSON.stringify({
|
||||||
|
k1: { id: 1, val: 'v1' },
|
||||||
|
k2: { id: 2, val: 'v2' },
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
|
const reader = await dbgateApi.jsonReader({
|
||||||
|
fileName: jsonFileName,
|
||||||
|
jsonStyle: 'object',
|
||||||
|
keyField: 'mykey',
|
||||||
|
});
|
||||||
|
|
||||||
|
const rows = await getReaderRows(reader);
|
||||||
|
|
||||||
|
expect(rows.length).toEqual(2);
|
||||||
|
expect(rows).toEqual([
|
||||||
|
{ mykey: 'k1', id: 1, val: 'v1' },
|
||||||
|
{ mykey: 'k2', id: 2, val: 'v2' },
|
||||||
|
]);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('JSON filtered object import test', async () => {
|
||||||
|
const jsonFileName = tmp.tmpNameSync();
|
||||||
|
|
||||||
|
fs.writeFileSync(
|
||||||
|
jsonFileName,
|
||||||
|
JSON.stringify({
|
||||||
|
filtered: {
|
||||||
|
k1: { id: 1, val: 'v1' },
|
||||||
|
k2: { id: 2, val: 'v2' },
|
||||||
|
},
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
|
const reader = await dbgateApi.jsonReader({
|
||||||
|
fileName: jsonFileName,
|
||||||
|
jsonStyle: 'object',
|
||||||
|
keyField: 'mykey',
|
||||||
|
rootField: 'filtered',
|
||||||
|
});
|
||||||
|
|
||||||
|
const rows = await getReaderRows(reader);
|
||||||
|
|
||||||
|
expect(rows.length).toEqual(2);
|
||||||
|
expect(rows).toEqual([
|
||||||
|
{ mykey: 'k1', id: 1, val: 'v1' },
|
||||||
|
{ mykey: 'k2', id: 2, val: 'v2' },
|
||||||
|
]);
|
||||||
|
});
|
||||||
@@ -22,6 +22,7 @@
|
|||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"cross-env": "^7.0.3",
|
"cross-env": "^7.0.3",
|
||||||
"jest": "^27.0.1",
|
"jest": "^27.0.1",
|
||||||
"pino-pretty": "^11.2.2"
|
"pino-pretty": "^11.2.2",
|
||||||
|
"tmp": "^0.2.3"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,10 @@
|
|||||||
|
global.DBGATE_PACKAGES = {
|
||||||
|
'dbgate-tools': require('dbgate-tools'),
|
||||||
|
'dbgate-sqltree': require('dbgate-sqltree'),
|
||||||
|
};
|
||||||
|
|
||||||
const { prettyFactory } = require('pino-pretty');
|
const { prettyFactory } = require('pino-pretty');
|
||||||
|
const tmp = require('tmp');
|
||||||
|
|
||||||
const pretty = prettyFactory({
|
const pretty = prettyFactory({
|
||||||
colorize: true,
|
colorize: true,
|
||||||
@@ -20,3 +26,5 @@ global.console = {
|
|||||||
process.stdout.write(messages.join(' ') + '\n');
|
process.stdout.write(messages.join(' ') + '\n');
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
tmp.setGracefulCleanup();
|
||||||
|
|||||||
@@ -1,8 +1,3 @@
|
|||||||
global.DBGATE_PACKAGES = {
|
|
||||||
'dbgate-tools': require('dbgate-tools'),
|
|
||||||
'dbgate-sqltree': require('dbgate-sqltree'),
|
|
||||||
};
|
|
||||||
|
|
||||||
const requireEngineDriver = require('dbgate-api/src/utility/requireEngineDriver');
|
const requireEngineDriver = require('dbgate-api/src/utility/requireEngineDriver');
|
||||||
const crypto = require('crypto');
|
const crypto = require('crypto');
|
||||||
|
|
||||||
|
|||||||
@@ -36,6 +36,7 @@
|
|||||||
"build:api": "yarn workspace dbgate-api build",
|
"build:api": "yarn workspace dbgate-api build",
|
||||||
"build:web:docker": "yarn workspace dbgate-web build",
|
"build:web:docker": "yarn workspace dbgate-web build",
|
||||||
"build:plugins:frontend": "workspaces-run --only=\"dbgate-plugin-*\" -- yarn build:frontend",
|
"build:plugins:frontend": "workspaces-run --only=\"dbgate-plugin-*\" -- yarn build:frontend",
|
||||||
|
"build:plugins:backend": "workspaces-run --only=\"dbgate-plugin-*\" -- yarn build:backend",
|
||||||
"build:plugins:frontend:watch": "workspaces-run --parallel --only=\"dbgate-plugin-*\" -- yarn build:frontend:watch",
|
"build:plugins:frontend:watch": "workspaces-run --parallel --only=\"dbgate-plugin-*\" -- yarn build:frontend:watch",
|
||||||
"storage-json": "dbmodel model-to-json storage-db packages/api/src/storageModel.js --commonjs",
|
"storage-json": "dbmodel model-to-json storage-db packages/api/src/storageModel.js --commonjs",
|
||||||
"plugins:copydist": "workspaces-run --only=\"dbgate-plugin-*\" -- yarn copydist",
|
"plugins:copydist": "workspaces-run --only=\"dbgate-plugin-*\" -- yarn copydist",
|
||||||
|
|||||||
@@ -57,6 +57,7 @@
|
|||||||
"rimraf": "^3.0.0",
|
"rimraf": "^3.0.0",
|
||||||
"simple-encryptor": "^4.0.0",
|
"simple-encryptor": "^4.0.0",
|
||||||
"ssh2": "^1.11.0",
|
"ssh2": "^1.11.0",
|
||||||
|
"stream-json": "^1.8.0",
|
||||||
"tar": "^6.0.5"
|
"tar": "^6.0.5"
|
||||||
},
|
},
|
||||||
"scripts": {
|
"scripts": {
|
||||||
|
|||||||
@@ -18,11 +18,14 @@ function readFirstLine(file) {
|
|||||||
}
|
}
|
||||||
if (reader.hasNextLine()) {
|
if (reader.hasNextLine()) {
|
||||||
reader.nextLine((err, line) => {
|
reader.nextLine((err, line) => {
|
||||||
if (err) reject(err);
|
if (err) {
|
||||||
resolve(line);
|
reader.close(() => reject(err)); // Ensure reader is closed on error
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
reader.close(() => resolve(line)); // Ensure reader is closed after reading
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
resolve(null);
|
reader.close(() => resolve(null)); // Properly close if no lines are present
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ const copyStream = require('./copyStream');
|
|||||||
const fakeObjectReader = require('./fakeObjectReader');
|
const fakeObjectReader = require('./fakeObjectReader');
|
||||||
const consoleObjectWriter = require('./consoleObjectWriter');
|
const consoleObjectWriter = require('./consoleObjectWriter');
|
||||||
const jsonLinesWriter = require('./jsonLinesWriter');
|
const jsonLinesWriter = require('./jsonLinesWriter');
|
||||||
const jsonArrayWriter = require('./jsonArrayWriter');
|
const jsonWriter = require('./jsonWriter');
|
||||||
const jsonLinesReader = require('./jsonLinesReader');
|
const jsonLinesReader = require('./jsonLinesReader');
|
||||||
const sqlDataWriter = require('./sqlDataWriter');
|
const sqlDataWriter = require('./sqlDataWriter');
|
||||||
const jslDataReader = require('./jslDataReader');
|
const jslDataReader = require('./jslDataReader');
|
||||||
@@ -29,6 +29,7 @@ const modifyJsonLinesReader = require('./modifyJsonLinesReader');
|
|||||||
const dataDuplicator = require('./dataDuplicator');
|
const dataDuplicator = require('./dataDuplicator');
|
||||||
const dbModelToJson = require('./dbModelToJson');
|
const dbModelToJson = require('./dbModelToJson');
|
||||||
const jsonToDbModel = require('./jsonToDbModel');
|
const jsonToDbModel = require('./jsonToDbModel');
|
||||||
|
const jsonReader = require('./jsonReader');
|
||||||
|
|
||||||
const dbgateApi = {
|
const dbgateApi = {
|
||||||
queryReader,
|
queryReader,
|
||||||
@@ -37,8 +38,9 @@ const dbgateApi = {
|
|||||||
tableReader,
|
tableReader,
|
||||||
copyStream,
|
copyStream,
|
||||||
jsonLinesWriter,
|
jsonLinesWriter,
|
||||||
jsonArrayWriter,
|
|
||||||
jsonLinesReader,
|
jsonLinesReader,
|
||||||
|
jsonReader,
|
||||||
|
jsonWriter,
|
||||||
sqlDataWriter,
|
sqlDataWriter,
|
||||||
fakeObjectReader,
|
fakeObjectReader,
|
||||||
consoleObjectWriter,
|
consoleObjectWriter,
|
||||||
|
|||||||
@@ -1,52 +0,0 @@
|
|||||||
const { getLogger } = require('dbgate-tools');
|
|
||||||
const fs = require('fs');
|
|
||||||
const stream = require('stream');
|
|
||||||
|
|
||||||
const logger = getLogger('jsonArrayWriter');
|
|
||||||
|
|
||||||
class StringifyStream extends stream.Transform {
|
|
||||||
constructor() {
|
|
||||||
super({ objectMode: true });
|
|
||||||
this.wasHeader = false;
|
|
||||||
this.wasRecord = false;
|
|
||||||
}
|
|
||||||
_transform(chunk, encoding, done) {
|
|
||||||
let skip = false;
|
|
||||||
|
|
||||||
if (!this.wasHeader) {
|
|
||||||
skip = chunk.__isStreamHeader;
|
|
||||||
this.wasHeader = true;
|
|
||||||
}
|
|
||||||
if (!skip) {
|
|
||||||
if (!this.wasRecord) {
|
|
||||||
this.push('[\n');
|
|
||||||
} else {
|
|
||||||
this.push(',\n');
|
|
||||||
}
|
|
||||||
this.wasRecord = true;
|
|
||||||
|
|
||||||
this.push(JSON.stringify(chunk));
|
|
||||||
}
|
|
||||||
done();
|
|
||||||
}
|
|
||||||
|
|
||||||
_flush(done) {
|
|
||||||
if (!this.wasRecord) {
|
|
||||||
this.push('[]\n');
|
|
||||||
} else {
|
|
||||||
this.push('\n]\n');
|
|
||||||
}
|
|
||||||
done();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async function jsonArrayWriter({ fileName, encoding = 'utf-8' }) {
|
|
||||||
logger.info(`Writing file ${fileName}`);
|
|
||||||
const stringify = new StringifyStream();
|
|
||||||
const fileStream = fs.createWriteStream(fileName, encoding);
|
|
||||||
stringify.pipe(fileStream);
|
|
||||||
stringify['finisher'] = fileStream;
|
|
||||||
return stringify;
|
|
||||||
}
|
|
||||||
|
|
||||||
module.exports = jsonArrayWriter;
|
|
||||||
@@ -2,6 +2,7 @@ const fs = require('fs');
|
|||||||
const stream = require('stream');
|
const stream = require('stream');
|
||||||
const byline = require('byline');
|
const byline = require('byline');
|
||||||
const { getLogger } = require('dbgate-tools');
|
const { getLogger } = require('dbgate-tools');
|
||||||
|
const download = require('./download');
|
||||||
const logger = getLogger('jsonLinesReader');
|
const logger = getLogger('jsonLinesReader');
|
||||||
|
|
||||||
class ParseStream extends stream.Transform {
|
class ParseStream extends stream.Transform {
|
||||||
@@ -35,8 +36,10 @@ class ParseStream extends stream.Transform {
|
|||||||
async function jsonLinesReader({ fileName, encoding = 'utf-8', limitRows = undefined }) {
|
async function jsonLinesReader({ fileName, encoding = 'utf-8', limitRows = undefined }) {
|
||||||
logger.info(`Reading file ${fileName}`);
|
logger.info(`Reading file ${fileName}`);
|
||||||
|
|
||||||
|
const downloadedFile = await download(fileName);
|
||||||
|
|
||||||
const fileStream = fs.createReadStream(
|
const fileStream = fs.createReadStream(
|
||||||
fileName,
|
downloadedFile,
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
encoding
|
encoding
|
||||||
);
|
);
|
||||||
|
|||||||
84
packages/api/src/shell/jsonReader.js
Normal file
84
packages/api/src/shell/jsonReader.js
Normal file
@@ -0,0 +1,84 @@
|
|||||||
|
const fs = require('fs');
|
||||||
|
const stream = require('stream');
|
||||||
|
const byline = require('byline');
|
||||||
|
const { getLogger } = require('dbgate-tools');
|
||||||
|
const { parser } = require('stream-json');
|
||||||
|
const { pick } = require('stream-json/filters/Pick');
|
||||||
|
const { streamArray } = require('stream-json/streamers/StreamArray');
|
||||||
|
const { streamObject } = require('stream-json/streamers/StreamObject');
|
||||||
|
const download = require('./download');
|
||||||
|
|
||||||
|
const logger = getLogger('jsonReader');
|
||||||
|
|
||||||
|
|
||||||
|
class ParseStream extends stream.Transform {
|
||||||
|
constructor({ limitRows, jsonStyle, keyField }) {
|
||||||
|
super({ objectMode: true });
|
||||||
|
this.wasHeader = false;
|
||||||
|
this.limitRows = limitRows;
|
||||||
|
this.jsonStyle = jsonStyle;
|
||||||
|
this.keyField = keyField || '_key';
|
||||||
|
this.rowsWritten = 0;
|
||||||
|
}
|
||||||
|
_transform(chunk, encoding, done) {
|
||||||
|
if (!this.wasHeader) {
|
||||||
|
this.push({
|
||||||
|
__isStreamHeader: true,
|
||||||
|
__isDynamicStructure: true,
|
||||||
|
});
|
||||||
|
|
||||||
|
this.wasHeader = true;
|
||||||
|
}
|
||||||
|
if (!this.limitRows || this.rowsWritten < this.limitRows) {
|
||||||
|
if (this.jsonStyle === 'object') {
|
||||||
|
this.push({
|
||||||
|
...chunk.value,
|
||||||
|
[this.keyField]: chunk.key,
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
this.push(chunk.value);
|
||||||
|
}
|
||||||
|
|
||||||
|
this.rowsWritten += 1;
|
||||||
|
}
|
||||||
|
done();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function jsonReader({
|
||||||
|
fileName,
|
||||||
|
jsonStyle,
|
||||||
|
keyField = '_key',
|
||||||
|
rootField = null,
|
||||||
|
encoding = 'utf-8',
|
||||||
|
limitRows = undefined,
|
||||||
|
}) {
|
||||||
|
logger.info(`Reading file ${fileName}`);
|
||||||
|
|
||||||
|
const downloadedFile = await download(fileName);
|
||||||
|
const fileStream = fs.createReadStream(
|
||||||
|
downloadedFile,
|
||||||
|
// @ts-ignore
|
||||||
|
encoding
|
||||||
|
);
|
||||||
|
const parseJsonStream = parser();
|
||||||
|
fileStream.pipe(parseJsonStream);
|
||||||
|
|
||||||
|
const parseStream = new ParseStream({ limitRows, jsonStyle, keyField });
|
||||||
|
|
||||||
|
const tramsformer = jsonStyle === 'object' ? streamObject() : streamArray();
|
||||||
|
|
||||||
|
if (rootField) {
|
||||||
|
const filterStream = pick({ filter: rootField });
|
||||||
|
parseJsonStream.pipe(filterStream);
|
||||||
|
filterStream.pipe(tramsformer);
|
||||||
|
} else {
|
||||||
|
parseJsonStream.pipe(tramsformer);
|
||||||
|
}
|
||||||
|
|
||||||
|
tramsformer.pipe(parseStream);
|
||||||
|
|
||||||
|
return parseStream;
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = jsonReader;
|
||||||
97
packages/api/src/shell/jsonWriter.js
Normal file
97
packages/api/src/shell/jsonWriter.js
Normal file
@@ -0,0 +1,97 @@
|
|||||||
|
const { getLogger } = require('dbgate-tools');
|
||||||
|
const fs = require('fs');
|
||||||
|
const stream = require('stream');
|
||||||
|
const _ = require('lodash');
|
||||||
|
|
||||||
|
const logger = getLogger('jsonArrayWriter');
|
||||||
|
|
||||||
|
class StringifyStream extends stream.Transform {
|
||||||
|
constructor({ jsonStyle, keyField, rootField }) {
|
||||||
|
super({ objectMode: true });
|
||||||
|
this.wasHeader = false;
|
||||||
|
this.wasRecord = false;
|
||||||
|
this.jsonStyle = jsonStyle;
|
||||||
|
this.keyField = keyField || '_key';
|
||||||
|
this.rootField = rootField;
|
||||||
|
}
|
||||||
|
_transform(chunk, encoding, done) {
|
||||||
|
let skip = false;
|
||||||
|
|
||||||
|
if (!this.wasHeader) {
|
||||||
|
skip = chunk.__isStreamHeader;
|
||||||
|
this.wasHeader = true;
|
||||||
|
}
|
||||||
|
if (!skip) {
|
||||||
|
if (!this.wasRecord) {
|
||||||
|
if (this.rootField) {
|
||||||
|
if (this.jsonStyle === 'object') {
|
||||||
|
this.push(`{"${this.rootField}": {\n`);
|
||||||
|
} else {
|
||||||
|
this.push(`{"${this.rootField}": [\n`);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (this.jsonStyle === 'object') {
|
||||||
|
this.push('{\n');
|
||||||
|
} else {
|
||||||
|
this.push('[\n');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
this.push(',\n');
|
||||||
|
}
|
||||||
|
this.wasRecord = true;
|
||||||
|
|
||||||
|
if (this.jsonStyle === 'object') {
|
||||||
|
const key = chunk[this.keyField] ?? chunk[Object.keys(chunk)[0]];
|
||||||
|
this.push(`"${key}": ${JSON.stringify(_.omit(chunk, [this.keyField]))}`);
|
||||||
|
} else {
|
||||||
|
this.push(JSON.stringify(chunk));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
done();
|
||||||
|
}
|
||||||
|
|
||||||
|
_flush(done) {
|
||||||
|
if (!this.wasRecord) {
|
||||||
|
if (this.rootField) {
|
||||||
|
if (this.jsonStyle === 'object') {
|
||||||
|
this.push(`{"${this.rootField}": {}}\n`);
|
||||||
|
} else {
|
||||||
|
this.push(`{"${this.rootField}": []}\n`);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (this.jsonStyle === 'object') {
|
||||||
|
this.push('{}\n');
|
||||||
|
} else {
|
||||||
|
this.push('[]\n');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (this.rootField) {
|
||||||
|
if (this.jsonStyle === 'object') {
|
||||||
|
this.push('\n}}\n');
|
||||||
|
} else {
|
||||||
|
this.push('\n]}\n');
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (this.jsonStyle === 'object') {
|
||||||
|
this.push('\n}\n');
|
||||||
|
} else {
|
||||||
|
this.push('\n]\n');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
done();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function jsonWriter({ fileName, jsonStyle, keyField = '_key', rootField, encoding = 'utf-8' }) {
|
||||||
|
logger.info(`Writing file ${fileName}`);
|
||||||
|
const stringify = new StringifyStream({ jsonStyle, keyField, rootField });
|
||||||
|
const fileStream = fs.createWriteStream(fileName, encoding);
|
||||||
|
stringify.pipe(fileStream);
|
||||||
|
stringify['finisher'] = fileStream;
|
||||||
|
return stringify;
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = jsonWriter;
|
||||||
@@ -66,7 +66,7 @@ class ParseStream extends stream.Transform {
|
|||||||
...obj,
|
...obj,
|
||||||
...update.fields,
|
...update.fields,
|
||||||
},
|
},
|
||||||
(v, k) => v.$$undefined$$
|
(v, k) => v?.$$undefined$$
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -65,18 +65,18 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import _ from 'lodash';
|
import _ from 'lodash';
|
||||||
import { filterName } from 'dbgate-tools';
|
import { filterName } from 'dbgate-tools';
|
||||||
import ImportExportModal from '../modals/ImportExportModal.svelte';
|
|
||||||
import { showModal } from '../modals/modalTools';
|
import { showModal } from '../modals/modalTools';
|
||||||
|
|
||||||
import { getExtensions } from '../stores';
|
import { getExtensions } from '../stores';
|
||||||
|
|
||||||
import createQuickExportMenu from '../utility/createQuickExportMenu';
|
import createQuickExportMenu from '../utility/createQuickExportMenu';
|
||||||
import { exportQuickExportFile } from '../utility/exportFileTools';
|
import { exportQuickExportFile, } from '../utility/exportFileTools';
|
||||||
import openNewTab from '../utility/openNewTab';
|
import openNewTab from '../utility/openNewTab';
|
||||||
import AppObjectCore from './AppObjectCore.svelte';
|
import AppObjectCore from './AppObjectCore.svelte';
|
||||||
import InputTextModal from '../modals/InputTextModal.svelte';
|
import InputTextModal from '../modals/InputTextModal.svelte';
|
||||||
import ConfirmModal from '../modals/ConfirmModal.svelte';
|
import ConfirmModal from '../modals/ConfirmModal.svelte';
|
||||||
import { apiCall } from '../utility/api';
|
import { apiCall } from '../utility/api';
|
||||||
|
import { openImportExportTab } from '../utility/importExportTools';
|
||||||
|
|
||||||
export let data;
|
export let data;
|
||||||
|
|
||||||
@@ -156,13 +156,19 @@
|
|||||||
{
|
{
|
||||||
text: 'Export',
|
text: 'Export',
|
||||||
onClick: () => {
|
onClick: () => {
|
||||||
showModal(ImportExportModal, {
|
openImportExportTab({
|
||||||
initialValues: {
|
sourceStorageType: 'archive',
|
||||||
sourceStorageType: 'archive',
|
sourceArchiveFolder: data.folderName,
|
||||||
sourceArchiveFolder: data.folderName,
|
sourceList: [data.fileName],
|
||||||
sourceList: [data.fileName],
|
|
||||||
},
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// showModal(ImportExportModal, {
|
||||||
|
// initialValues: {
|
||||||
|
// sourceStorageType: 'archive',
|
||||||
|
// sourceArchiveFolder: data.folderName,
|
||||||
|
// sourceList: [data.fileName],
|
||||||
|
// },
|
||||||
|
// });
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
),
|
),
|
||||||
|
|||||||
@@ -98,25 +98,39 @@
|
|||||||
};
|
};
|
||||||
|
|
||||||
const handleImport = () => {
|
const handleImport = () => {
|
||||||
showModal(ImportExportModal, {
|
openImportExportTab({
|
||||||
initialValues: {
|
sourceStorageType: getDefaultFileFormat($extensions).storageType,
|
||||||
sourceStorageType: getDefaultFileFormat($extensions).storageType,
|
targetStorageType: 'database',
|
||||||
targetStorageType: 'database',
|
targetConnectionId: connection._id,
|
||||||
targetConnectionId: connection._id,
|
targetDatabaseName: name,
|
||||||
targetDatabaseName: name,
|
|
||||||
},
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// showModal(ImportExportModal, {
|
||||||
|
// initialValues: {
|
||||||
|
// sourceStorageType: getDefaultFileFormat($extensions).storageType,
|
||||||
|
// targetStorageType: 'database',
|
||||||
|
// targetConnectionId: connection._id,
|
||||||
|
// targetDatabaseName: name,
|
||||||
|
// },
|
||||||
|
// });
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleExport = () => {
|
const handleExport = () => {
|
||||||
showModal(ImportExportModal, {
|
openImportExportTab({
|
||||||
initialValues: {
|
targetStorageType: getDefaultFileFormat($extensions).storageType,
|
||||||
targetStorageType: getDefaultFileFormat($extensions).storageType,
|
sourceStorageType: 'database',
|
||||||
sourceStorageType: 'database',
|
sourceConnectionId: connection._id,
|
||||||
sourceConnectionId: connection._id,
|
sourceDatabaseName: name,
|
||||||
sourceDatabaseName: name,
|
|
||||||
},
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// showModal(ImportExportModal, {
|
||||||
|
// initialValues: {
|
||||||
|
// targetStorageType: getDefaultFileFormat($extensions).storageType,
|
||||||
|
// sourceStorageType: 'database',
|
||||||
|
// sourceConnectionId: connection._id,
|
||||||
|
// sourceDatabaseName: name,
|
||||||
|
// },
|
||||||
|
// });
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleSqlGenerator = () => {
|
const handleSqlGenerator = () => {
|
||||||
@@ -360,7 +374,6 @@
|
|||||||
import uuidv1 from 'uuid/v1';
|
import uuidv1 from 'uuid/v1';
|
||||||
|
|
||||||
import _, { find } from 'lodash';
|
import _, { find } from 'lodash';
|
||||||
import ImportExportModal from '../modals/ImportExportModal.svelte';
|
|
||||||
import { showModal } from '../modals/modalTools';
|
import { showModal } from '../modals/modalTools';
|
||||||
import SqlGeneratorModal from '../modals/SqlGeneratorModal.svelte';
|
import SqlGeneratorModal from '../modals/SqlGeneratorModal.svelte';
|
||||||
import { getDefaultFileFormat } from '../plugins/fileformats';
|
import { getDefaultFileFormat } from '../plugins/fileformats';
|
||||||
@@ -390,13 +403,13 @@
|
|||||||
import ConfirmSqlModal, { runOperationOnDatabase, saveScriptToDatabase } from '../modals/ConfirmSqlModal.svelte';
|
import ConfirmSqlModal, { runOperationOnDatabase, saveScriptToDatabase } from '../modals/ConfirmSqlModal.svelte';
|
||||||
import { filterAppsForDatabase } from '../utility/appTools';
|
import { filterAppsForDatabase } from '../utility/appTools';
|
||||||
import newQuery from '../query/newQuery';
|
import newQuery from '../query/newQuery';
|
||||||
import { exportSqlDump } from '../utility/exportFileTools';
|
|
||||||
import ImportDatabaseDumpModal from '../modals/ImportDatabaseDumpModal.svelte';
|
import ImportDatabaseDumpModal from '../modals/ImportDatabaseDumpModal.svelte';
|
||||||
import ExportDatabaseDumpModal from '../modals/ExportDatabaseDumpModal.svelte';
|
import ExportDatabaseDumpModal from '../modals/ExportDatabaseDumpModal.svelte';
|
||||||
import ConfirmModal from '../modals/ConfirmModal.svelte';
|
import ConfirmModal from '../modals/ConfirmModal.svelte';
|
||||||
import { closeMultipleTabs } from '../tabpanel/TabsPanel.svelte';
|
import { closeMultipleTabs } from '../tabpanel/TabsPanel.svelte';
|
||||||
import NewCollectionModal from '../modals/NewCollectionModal.svelte';
|
import NewCollectionModal from '../modals/NewCollectionModal.svelte';
|
||||||
import hasPermission from '../utility/hasPermission';
|
import hasPermission from '../utility/hasPermission';
|
||||||
|
import { openImportExportTab } from '../utility/importExportTools';
|
||||||
|
|
||||||
export let data;
|
export let data;
|
||||||
export let passProps;
|
export let passProps;
|
||||||
|
|||||||
@@ -618,15 +618,22 @@
|
|||||||
});
|
});
|
||||||
} else if (menu.isImport) {
|
} else if (menu.isImport) {
|
||||||
const { conid, database } = data;
|
const { conid, database } = data;
|
||||||
showModal(ImportExportModal, {
|
openImportExportTab({
|
||||||
initialValues: {
|
sourceStorageType: getDefaultFileFormat(getExtensions()).storageType,
|
||||||
sourceStorageType: getDefaultFileFormat(getExtensions()).storageType,
|
targetStorageType: 'database',
|
||||||
targetStorageType: 'database',
|
targetConnectionId: conid,
|
||||||
targetConnectionId: conid,
|
targetDatabaseName: database,
|
||||||
targetDatabaseName: database,
|
fixedTargetPureName: data.pureName,
|
||||||
fixedTargetPureName: data.pureName,
|
|
||||||
},
|
|
||||||
});
|
});
|
||||||
|
// showModal(ImportExportModal, {
|
||||||
|
// initialValues: {
|
||||||
|
// sourceStorageType: getDefaultFileFormat(getExtensions()).storageType,
|
||||||
|
// targetStorageType: 'database',
|
||||||
|
// targetConnectionId: conid,
|
||||||
|
// targetDatabaseName: database,
|
||||||
|
// fixedTargetPureName: data.pureName,
|
||||||
|
// },
|
||||||
|
// });
|
||||||
} else {
|
} else {
|
||||||
openDatabaseObjectDetail(
|
openDatabaseObjectDetail(
|
||||||
menu.tab,
|
menu.tab,
|
||||||
@@ -764,31 +771,22 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
onClick: () => {
|
onClick: () => {
|
||||||
// openNewTab(
|
openImportExportTab({
|
||||||
// {
|
sourceStorageType: 'database',
|
||||||
// tabComponent: 'ImportExportTab',
|
sourceConnectionId: data.conid,
|
||||||
// title: 'Import/Export',
|
sourceDatabaseName: data.database,
|
||||||
// icon: 'img export',
|
sourceSchemaName: data.schemaName,
|
||||||
// },
|
sourceList: [data.pureName],
|
||||||
// {
|
|
||||||
// editor: {
|
|
||||||
// sourceStorageType: 'database',
|
|
||||||
// sourceConnectionId: data.conid,
|
|
||||||
// sourceDatabaseName: data.database,
|
|
||||||
// sourceSchemaName: data.schemaName,
|
|
||||||
// sourceList: [data.pureName],
|
|
||||||
// },
|
|
||||||
// }
|
|
||||||
// );
|
|
||||||
showModal(ImportExportModal, {
|
|
||||||
initialValues: {
|
|
||||||
sourceStorageType: 'database',
|
|
||||||
sourceConnectionId: data.conid,
|
|
||||||
sourceDatabaseName: data.database,
|
|
||||||
sourceSchemaName: data.schemaName,
|
|
||||||
sourceList: [data.pureName],
|
|
||||||
},
|
|
||||||
});
|
});
|
||||||
|
// showModal(ImportExportModal, {
|
||||||
|
// initialValues: {
|
||||||
|
// sourceStorageType: 'database',
|
||||||
|
// sourceConnectionId: data.conid,
|
||||||
|
// sourceDatabaseName: data.database,
|
||||||
|
// sourceSchemaName: data.schemaName,
|
||||||
|
// sourceList: [data.pureName],
|
||||||
|
// },
|
||||||
|
// });
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
@@ -832,7 +830,6 @@
|
|||||||
import { filterName, generateDbPairingId, getAlterDatabaseScript, getConnectionLabel } from 'dbgate-tools';
|
import { filterName, generateDbPairingId, getAlterDatabaseScript, getConnectionLabel } from 'dbgate-tools';
|
||||||
import { getConnectionInfo, getDatabaseInfo } from '../utility/metadataLoaders';
|
import { getConnectionInfo, getDatabaseInfo } from '../utility/metadataLoaders';
|
||||||
import fullDisplayName from '../utility/fullDisplayName';
|
import fullDisplayName from '../utility/fullDisplayName';
|
||||||
import ImportExportModal from '../modals/ImportExportModal.svelte';
|
|
||||||
import { showModal } from '../modals/modalTools';
|
import { showModal } from '../modals/modalTools';
|
||||||
import { findEngineDriver } from 'dbgate-tools';
|
import { findEngineDriver } from 'dbgate-tools';
|
||||||
import uuidv1 from 'uuid/v1';
|
import uuidv1 from 'uuid/v1';
|
||||||
@@ -848,6 +845,7 @@
|
|||||||
import { format as dateFormat } from 'date-fns';
|
import { format as dateFormat } from 'date-fns';
|
||||||
import { getDefaultFileFormat } from '../plugins/fileformats';
|
import { getDefaultFileFormat } from '../plugins/fileformats';
|
||||||
import hasPermission from '../utility/hasPermission';
|
import hasPermission from '../utility/hasPermission';
|
||||||
|
import { openImportExportTab } from '../utility/importExportTools';
|
||||||
|
|
||||||
export let data;
|
export let data;
|
||||||
export let passProps;
|
export let passProps;
|
||||||
|
|||||||
@@ -65,6 +65,14 @@
|
|||||||
currentConnection: true,
|
currentConnection: true,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const jobs: FileTypeHandler = {
|
||||||
|
icon: 'img export',
|
||||||
|
format: 'json',
|
||||||
|
tabComponent: 'ImportExportTab',
|
||||||
|
folder: 'jobs',
|
||||||
|
currentConnection: false,
|
||||||
|
};
|
||||||
|
|
||||||
const perspectives: FileTypeHandler = {
|
const perspectives: FileTypeHandler = {
|
||||||
icon: 'img perspective',
|
icon: 'img perspective',
|
||||||
format: 'json',
|
format: 'json',
|
||||||
@@ -82,6 +90,7 @@
|
|||||||
sqlite,
|
sqlite,
|
||||||
diagrams,
|
diagrams,
|
||||||
perspectives,
|
perspectives,
|
||||||
|
jobs,
|
||||||
};
|
};
|
||||||
|
|
||||||
export const extractKey = data => data.file;
|
export const extractKey = data => data.file;
|
||||||
|
|||||||
@@ -13,7 +13,6 @@ import registerCommand from './registerCommand';
|
|||||||
import { get } from 'svelte/store';
|
import { get } from 'svelte/store';
|
||||||
import AboutModal from '../modals/AboutModal.svelte';
|
import AboutModal from '../modals/AboutModal.svelte';
|
||||||
import SettingsModal from '../settings/SettingsModal.svelte';
|
import SettingsModal from '../settings/SettingsModal.svelte';
|
||||||
import ImportExportModal from '../modals/ImportExportModal.svelte';
|
|
||||||
import SqlGeneratorModal from '../modals/SqlGeneratorModal.svelte';
|
import SqlGeneratorModal from '../modals/SqlGeneratorModal.svelte';
|
||||||
import { showModal } from '../modals/modalTools';
|
import { showModal } from '../modals/modalTools';
|
||||||
import newQuery, { newDiagram, newPerspective, newQueryDesign } from '../query/newQuery';
|
import newQuery, { newDiagram, newPerspective, newQueryDesign } from '../query/newQuery';
|
||||||
@@ -44,6 +43,7 @@ import ErrorMessageModal from '../modals/ErrorMessageModal.svelte';
|
|||||||
import NewCollectionModal from '../modals/NewCollectionModal.svelte';
|
import NewCollectionModal from '../modals/NewCollectionModal.svelte';
|
||||||
import ConfirmModal from '../modals/ConfirmModal.svelte';
|
import ConfirmModal from '../modals/ConfirmModal.svelte';
|
||||||
import localforage from 'localforage';
|
import localforage from 'localforage';
|
||||||
|
import { openImportExportTab } from '../utility/importExportTools';
|
||||||
|
|
||||||
// function themeCommand(theme: ThemeDefinition) {
|
// function themeCommand(theme: ThemeDefinition) {
|
||||||
// return {
|
// return {
|
||||||
@@ -483,10 +483,18 @@ registerCommand({
|
|||||||
toolbar: true,
|
toolbar: true,
|
||||||
icon: 'icon import',
|
icon: 'icon import',
|
||||||
onClick: () =>
|
onClick: () =>
|
||||||
showModal(ImportExportModal, {
|
openImportExportTab(
|
||||||
importToCurrentTarget: true,
|
{
|
||||||
initialValues: { sourceStorageType: getDefaultFileFormat(get(extensions)).storageType },
|
sourceStorageType: getDefaultFileFormat(get(extensions)).storageType,
|
||||||
}),
|
},
|
||||||
|
{
|
||||||
|
importToCurrentTarget: true,
|
||||||
|
}
|
||||||
|
),
|
||||||
|
// showModal(ImportExportModal, {
|
||||||
|
// importToCurrentTarget: true,
|
||||||
|
// initialValues: { sourceStorageType: getDefaultFileFormat(get(extensions)).storageType },
|
||||||
|
// }),
|
||||||
});
|
});
|
||||||
|
|
||||||
registerCommand({
|
registerCommand({
|
||||||
@@ -595,8 +603,6 @@ registerCommand({
|
|||||||
onClick: () => getElectron().send('check-for-updates'),
|
onClick: () => getElectron().send('check-for-updates'),
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
export function registerFileCommands({
|
export function registerFileCommands({
|
||||||
idPrefix,
|
idPrefix,
|
||||||
category,
|
category,
|
||||||
|
|||||||
@@ -122,8 +122,6 @@
|
|||||||
import { registerQuickExportHandler } from '../buttons/ToolStripExportButton.svelte';
|
import { registerQuickExportHandler } from '../buttons/ToolStripExportButton.svelte';
|
||||||
import registerCommand from '../commands/registerCommand';
|
import registerCommand from '../commands/registerCommand';
|
||||||
import { extractShellConnection } from '../impexp/createImpExpScript';
|
import { extractShellConnection } from '../impexp/createImpExpScript';
|
||||||
import ImportExportModal from '../modals/ImportExportModal.svelte';
|
|
||||||
import { showModal } from '../modals/modalTools';
|
|
||||||
import { apiCall } from '../utility/api';
|
import { apiCall } from '../utility/api';
|
||||||
|
|
||||||
import { registerMenu } from '../utility/contextMenu';
|
import { registerMenu } from '../utility/contextMenu';
|
||||||
@@ -136,6 +134,7 @@
|
|||||||
|
|
||||||
import LoadingDataGridCore from './LoadingDataGridCore.svelte';
|
import LoadingDataGridCore from './LoadingDataGridCore.svelte';
|
||||||
import { mongoFilterBehaviour, standardFilterBehaviours } from 'dbgate-tools';
|
import { mongoFilterBehaviour, standardFilterBehaviours } from 'dbgate-tools';
|
||||||
|
import { openImportExportTab } from '../utility/importExportTools';
|
||||||
|
|
||||||
export let conid;
|
export let conid;
|
||||||
export let display;
|
export let display;
|
||||||
@@ -207,7 +206,8 @@
|
|||||||
initialValues.sourceQueryType = coninfo.isReadOnly ? 'json' : 'native';
|
initialValues.sourceQueryType = coninfo.isReadOnly ? 'json' : 'native';
|
||||||
initialValues.sourceList = [pureName];
|
initialValues.sourceList = [pureName];
|
||||||
initialValues[`columns_${pureName}`] = display.getExportColumnMap();
|
initialValues[`columns_${pureName}`] = display.getExportColumnMap();
|
||||||
showModal(ImportExportModal, { initialValues });
|
openImportExportTab(initialValues);
|
||||||
|
// showModal(ImportExportModal, { initialValues });
|
||||||
}
|
}
|
||||||
|
|
||||||
export function openQuery() {
|
export function openQuery() {
|
||||||
|
|||||||
@@ -45,9 +45,6 @@
|
|||||||
import _ from 'lodash';
|
import _ from 'lodash';
|
||||||
import { registerQuickExportHandler } from '../buttons/ToolStripExportButton.svelte';
|
import { registerQuickExportHandler } from '../buttons/ToolStripExportButton.svelte';
|
||||||
import registerCommand from '../commands/registerCommand';
|
import registerCommand from '../commands/registerCommand';
|
||||||
import ImportExportModal from '../modals/ImportExportModal.svelte';
|
|
||||||
import { showModal } from '../modals/modalTools';
|
|
||||||
import { extensions } from '../stores';
|
|
||||||
import { apiCall, apiOff, apiOn } from '../utility/api';
|
import { apiCall, apiOff, apiOn } from '../utility/api';
|
||||||
|
|
||||||
import { registerMenu } from '../utility/contextMenu';
|
import { registerMenu } from '../utility/contextMenu';
|
||||||
@@ -58,7 +55,7 @@
|
|||||||
import ChangeSetGrider from './ChangeSetGrider';
|
import ChangeSetGrider from './ChangeSetGrider';
|
||||||
|
|
||||||
import LoadingDataGridCore from './LoadingDataGridCore.svelte';
|
import LoadingDataGridCore from './LoadingDataGridCore.svelte';
|
||||||
import RowsArrayGrider from './RowsArrayGrider';
|
import { openImportExportTab } from '../utility/importExportTools';
|
||||||
|
|
||||||
export let jslid;
|
export let jslid;
|
||||||
export let display;
|
export let display;
|
||||||
@@ -152,7 +149,8 @@
|
|||||||
initialValues.sourceList = ['query-data'];
|
initialValues.sourceList = ['query-data'];
|
||||||
initialValues[`columns_query-data`] = display.getExportColumnMap();
|
initialValues[`columns_query-data`] = display.getExportColumnMap();
|
||||||
}
|
}
|
||||||
showModal(ImportExportModal, { initialValues });
|
openImportExportTab(initialValues);
|
||||||
|
// showModal(ImportExportModal, { initialValues });
|
||||||
}
|
}
|
||||||
|
|
||||||
const quickExportHandler = fmt => async () => {
|
const quickExportHandler = fmt => async () => {
|
||||||
|
|||||||
@@ -65,13 +65,10 @@
|
|||||||
|
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import _ from 'lodash';
|
import _ from 'lodash';
|
||||||
import { getContext } from 'svelte';
|
|
||||||
import { registerQuickExportHandler } from '../buttons/ToolStripExportButton.svelte';
|
import { registerQuickExportHandler } from '../buttons/ToolStripExportButton.svelte';
|
||||||
|
|
||||||
import registerCommand from '../commands/registerCommand';
|
import registerCommand from '../commands/registerCommand';
|
||||||
import { extractShellConnection } from '../impexp/createImpExpScript';
|
import { extractShellConnection } from '../impexp/createImpExpScript';
|
||||||
import ImportExportModal from '../modals/ImportExportModal.svelte';
|
|
||||||
import { showModal } from '../modals/modalTools';
|
|
||||||
import { apiCall } from '../utility/api';
|
import { apiCall } from '../utility/api';
|
||||||
|
|
||||||
import { registerMenu } from '../utility/contextMenu';
|
import { registerMenu } from '../utility/contextMenu';
|
||||||
@@ -84,6 +81,7 @@
|
|||||||
|
|
||||||
import LoadingDataGridCore from './LoadingDataGridCore.svelte';
|
import LoadingDataGridCore from './LoadingDataGridCore.svelte';
|
||||||
import hasPermission from '../utility/hasPermission';
|
import hasPermission from '../utility/hasPermission';
|
||||||
|
import { openImportExportTab } from '../utility/importExportTools';
|
||||||
|
|
||||||
export let conid;
|
export let conid;
|
||||||
export let display;
|
export let display;
|
||||||
@@ -145,7 +143,8 @@
|
|||||||
initialValues.sourceQueryType = coninfo.isReadOnly ? 'json' : 'native';
|
initialValues.sourceQueryType = coninfo.isReadOnly ? 'json' : 'native';
|
||||||
initialValues.sourceList = display.baseTableOrSimilar ? [display.baseTableOrSimilar.pureName] : [];
|
initialValues.sourceList = display.baseTableOrSimilar ? [display.baseTableOrSimilar.pureName] : [];
|
||||||
initialValues[`columns_${pureName}`] = display.getExportColumnMap();
|
initialValues[`columns_${pureName}`] = display.getExportColumnMap();
|
||||||
showModal(ImportExportModal, { initialValues });
|
openImportExportTab(initialValues);
|
||||||
|
// showModal(ImportExportModal, { initialValues });
|
||||||
}
|
}
|
||||||
|
|
||||||
export function openQuery() {
|
export function openQuery() {
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
<script lang="ts" context="module">
|
<script lang="ts" context="module">
|
||||||
function extractUrlName(url, values) {
|
function extractUrlName(url, values) {
|
||||||
const match = url.match(/\/([^/]+)($|\?)/);
|
const match = url.match(/\/([^/\?]+)($|\?)/);
|
||||||
if (match) {
|
if (match) {
|
||||||
const res = match[1];
|
const res = match[1];
|
||||||
if (res.includes('.')) {
|
if (res.includes('.')) {
|
||||||
|
|||||||
@@ -46,7 +46,6 @@
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { onMount } from 'svelte';
|
|
||||||
import { writable } from 'svelte/store';
|
import { writable } from 'svelte/store';
|
||||||
import Link from '../elements/Link.svelte';
|
import Link from '../elements/Link.svelte';
|
||||||
import TableControl from '../elements/TableControl.svelte';
|
import TableControl from '../elements/TableControl.svelte';
|
||||||
@@ -67,10 +66,12 @@
|
|||||||
import SourceName from './SourceName.svelte';
|
import SourceName from './SourceName.svelte';
|
||||||
|
|
||||||
import SourceTargetConfig from './SourceTargetConfig.svelte';
|
import SourceTargetConfig from './SourceTargetConfig.svelte';
|
||||||
|
import useEffect from '../utility/useEffect';
|
||||||
|
|
||||||
export let uploadedFile = undefined;
|
// export let uploadedFile = undefined;
|
||||||
export let openedFile = undefined;
|
// export let openedFile = undefined;
|
||||||
export let previewReaderStore;
|
export let previewReaderStore;
|
||||||
|
export let isTabActive;
|
||||||
|
|
||||||
const { values, setFieldValue } = getFormContext();
|
const { values, setFieldValue } = getFormContext();
|
||||||
|
|
||||||
@@ -98,7 +99,7 @@
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleUpload = file => {
|
export function addUploadedFile(file) {
|
||||||
addFilesToSourceList(
|
addFilesToSourceList(
|
||||||
$extensions,
|
$extensions,
|
||||||
[
|
[
|
||||||
@@ -113,36 +114,20 @@
|
|||||||
previewSource.set
|
previewSource.set
|
||||||
);
|
);
|
||||||
// setFieldValue('sourceList', [...(sourceList || []), file.originalName]);
|
// setFieldValue('sourceList', [...(sourceList || []), file.originalName]);
|
||||||
};
|
}
|
||||||
|
|
||||||
onMount(() => {
|
$: effectActiveTab = useEffect(() => {
|
||||||
setUploadListener(handleUpload);
|
if (isTabActive) {
|
||||||
if (uploadedFile) {
|
setUploadListener(addUploadedFile);
|
||||||
handleUpload(uploadedFile);
|
return () => {
|
||||||
|
setUploadListener(null);
|
||||||
|
};
|
||||||
|
} else {
|
||||||
|
return () => {};
|
||||||
}
|
}
|
||||||
if (openedFile) {
|
|
||||||
handleUpload(openedFile);
|
|
||||||
// addFilesToSourceList(
|
|
||||||
// $extensions,
|
|
||||||
// [
|
|
||||||
// {
|
|
||||||
// fileName: openedFile.filePath,
|
|
||||||
// shortName: openedFile.shortName,
|
|
||||||
// },
|
|
||||||
// ],
|
|
||||||
// $values,
|
|
||||||
// values,
|
|
||||||
// !sourceList || sourceList.length == 0 ? openedFile.storageType : null,
|
|
||||||
// previewSource.set
|
|
||||||
// );
|
|
||||||
}
|
|
||||||
|
|
||||||
return () => {
|
|
||||||
setUploadListener(null);
|
|
||||||
};
|
|
||||||
});
|
});
|
||||||
// engine={sourceEngine}
|
|
||||||
// {setPreviewSource}
|
$effectActiveTab;
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<div class="flex1">
|
<div class="flex1">
|
||||||
|
|||||||
@@ -196,6 +196,7 @@
|
|||||||
width: 20vw;
|
width: 20vw;
|
||||||
margin-left: var(--dim-large-form-margin);
|
margin-left: var(--dim-large-form-margin);
|
||||||
margin-bottom: var(--dim-large-form-margin);
|
margin-bottom: var(--dim-large-form-margin);
|
||||||
|
border: 1px solid var(--theme-border);
|
||||||
}
|
}
|
||||||
|
|
||||||
.label {
|
.label {
|
||||||
|
|||||||
@@ -192,7 +192,7 @@ export function normalizeExportColumnMap(colmap) {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
export default async function createImpExpScript(extensions, values, addEditorInfo = true, forceScript = false) {
|
export default async function createImpExpScript(extensions, values, forceScript = false) {
|
||||||
const config = getCurrentConfig();
|
const config = getCurrentConfig();
|
||||||
const script =
|
const script =
|
||||||
config.allowShellScripting || forceScript
|
config.allowShellScripting || forceScript
|
||||||
@@ -233,10 +233,6 @@ export default async function createImpExpScript(extensions, values, addEditorIn
|
|||||||
script.copyStream(sourceVar, targetVar, colmapVar);
|
script.copyStream(sourceVar, targetVar, colmapVar);
|
||||||
script.endLine();
|
script.endLine();
|
||||||
}
|
}
|
||||||
if (addEditorInfo) {
|
|
||||||
script.comment('@ImportExportConfigurator');
|
|
||||||
script.comment(JSON.stringify(values));
|
|
||||||
}
|
|
||||||
return script.getScript(values.schedule);
|
return script.getScript(values.schedule);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -8,6 +8,7 @@
|
|||||||
import { closeCurrentModal } from './modalTools';
|
import { closeCurrentModal } from './modalTools';
|
||||||
|
|
||||||
export let onConfirm;
|
export let onConfirm;
|
||||||
|
export let url;
|
||||||
|
|
||||||
const handleSubmit = e => {
|
const handleSubmit = e => {
|
||||||
onConfirm(e.detail.url);
|
onConfirm(e.detail.url);
|
||||||
@@ -15,7 +16,7 @@
|
|||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<FormProvider>
|
<FormProvider initialValues={{ url }}>
|
||||||
<ModalBase {...$$restProps}>
|
<ModalBase {...$$restProps}>
|
||||||
<svelte:fragment slot="header">Download imported file from web</svelte:fragment>
|
<svelte:fragment slot="header">Download imported file from web</svelte:fragment>
|
||||||
|
|
||||||
|
|||||||
@@ -1,203 +0,0 @@
|
|||||||
<script lang="ts">
|
|
||||||
import moment from 'moment';
|
|
||||||
import { writable } from 'svelte/store';
|
|
||||||
import HorizontalSplitter from '../elements/HorizontalSplitter.svelte';
|
|
||||||
import LargeButton from '../buttons/LargeButton.svelte';
|
|
||||||
import LoadingInfo from '../elements/LoadingInfo.svelte';
|
|
||||||
import VerticalSplitter from '../elements/VerticalSplitter.svelte';
|
|
||||||
|
|
||||||
import FormProvider from '../forms/FormProvider.svelte';
|
|
||||||
import FormTextField from '../forms/FormTextField.svelte';
|
|
||||||
import LargeFormButton from '../forms/LargeFormButton.svelte';
|
|
||||||
import FontIcon from '../icons/FontIcon.svelte';
|
|
||||||
import createImpExpScript from '../impexp/createImpExpScript';
|
|
||||||
import ImportExportConfigurator from '../impexp/ImportExportConfigurator.svelte';
|
|
||||||
import PreviewDataGrid from '../impexp/PreviewDataGrid.svelte';
|
|
||||||
import { getDefaultFileFormat } from '../plugins/fileformats';
|
|
||||||
import RunnerOutputFiles from '../query/RunnerOutputFiles.svelte';
|
|
||||||
import SocketMessageView from '../query/SocketMessageView.svelte';
|
|
||||||
import { currentArchive, currentDatabase, extensions, visibleWidgetSideBar, selectedWidget } from '../stores';
|
|
||||||
import { apiCall, apiOff, apiOn } from '../utility/api';
|
|
||||||
import createRef from '../utility/createRef';
|
|
||||||
import openNewTab from '../utility/openNewTab';
|
|
||||||
import useEffect from '../utility/useEffect';
|
|
||||||
import WidgetColumnBar from '../widgets/WidgetColumnBar.svelte';
|
|
||||||
import WidgetColumnBarItem from '../widgets/WidgetColumnBarItem.svelte';
|
|
||||||
import ModalBase from './ModalBase.svelte';
|
|
||||||
import { closeCurrentModal } from './modalTools';
|
|
||||||
|
|
||||||
let busy = false;
|
|
||||||
let executeNumber = 0;
|
|
||||||
let runnerId = null;
|
|
||||||
|
|
||||||
const previewReaderStore = writable(null);
|
|
||||||
|
|
||||||
export let initialValues;
|
|
||||||
export let uploadedFile = undefined;
|
|
||||||
export let openedFile = undefined;
|
|
||||||
export let importToCurrentTarget = false;
|
|
||||||
|
|
||||||
const refreshArchiveFolderRef = createRef(null);
|
|
||||||
|
|
||||||
function detectCurrentTarget() {
|
|
||||||
if (!importToCurrentTarget) return {};
|
|
||||||
|
|
||||||
if ($currentDatabase && $selectedWidget != 'archive') {
|
|
||||||
return {
|
|
||||||
targetStorageType: 'database',
|
|
||||||
targetConnectionId: $currentDatabase?.connection?._id,
|
|
||||||
targetDatabaseName: $currentDatabase?.name,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($currentArchive == 'default') {
|
|
||||||
return {
|
|
||||||
targetStorageType: 'archive',
|
|
||||||
targetArchiveFolder: `import-${moment().format('YYYY-MM-DD-hh-mm-ss')}`,
|
|
||||||
};
|
|
||||||
} else {
|
|
||||||
return {
|
|
||||||
targetStorageType: 'archive',
|
|
||||||
targetArchiveFolder: $currentArchive,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
$: effect = useEffect(() => registerRunnerDone(runnerId));
|
|
||||||
|
|
||||||
function registerRunnerDone(rid) {
|
|
||||||
if (rid) {
|
|
||||||
apiOn(`runner-done-${rid}`, handleRunnerDone);
|
|
||||||
return () => {
|
|
||||||
apiOff(`runner-done-${rid}`, handleRunnerDone);
|
|
||||||
};
|
|
||||||
} else {
|
|
||||||
return () => {};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
$: $effect;
|
|
||||||
|
|
||||||
const handleRunnerDone = () => {
|
|
||||||
busy = false;
|
|
||||||
if (refreshArchiveFolderRef.get()) {
|
|
||||||
apiCall('archive/refresh-folders', {});
|
|
||||||
apiCall('archive/refresh-files', { folder: refreshArchiveFolderRef.get() });
|
|
||||||
$currentArchive = refreshArchiveFolderRef.get();
|
|
||||||
$selectedWidget = 'archive';
|
|
||||||
$visibleWidgetSideBar = true;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const handleGenerateScript = async e => {
|
|
||||||
closeCurrentModal();
|
|
||||||
const code = await createImpExpScript($extensions, e.detail, undefined, true);
|
|
||||||
openNewTab(
|
|
||||||
{
|
|
||||||
title: 'Shell #',
|
|
||||||
icon: 'img shell',
|
|
||||||
tabComponent: 'ShellTab',
|
|
||||||
},
|
|
||||||
{ editor: code }
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
const handleExecute = async e => {
|
|
||||||
if (busy) return;
|
|
||||||
const values = e.detail;
|
|
||||||
busy = true;
|
|
||||||
const script = await createImpExpScript($extensions, values);
|
|
||||||
executeNumber += 1;
|
|
||||||
let runid = runnerId;
|
|
||||||
const resp = await apiCall('runners/start', { script });
|
|
||||||
runid = resp.runid;
|
|
||||||
runnerId = runid;
|
|
||||||
|
|
||||||
if (values.targetStorageType == 'archive') {
|
|
||||||
refreshArchiveFolderRef.set(values.targetArchiveFolder);
|
|
||||||
} else {
|
|
||||||
refreshArchiveFolderRef.set(null);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const handleCancel = () => {
|
|
||||||
apiCall('runners/cancel', {
|
|
||||||
runid: runnerId,
|
|
||||||
});
|
|
||||||
};
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<FormProvider
|
|
||||||
initialValues={{
|
|
||||||
sourceStorageType: 'database',
|
|
||||||
targetStorageType: getDefaultFileFormat($extensions).storageType,
|
|
||||||
targetArchiveFolder: $currentArchive,
|
|
||||||
sourceArchiveFolder: $currentArchive,
|
|
||||||
...detectCurrentTarget(),
|
|
||||||
...initialValues,
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<ModalBase {...$$restProps} fullScreen>
|
|
||||||
<svelte:fragment slot="header">
|
|
||||||
Import/Export
|
|
||||||
{#if busy}
|
|
||||||
<FontIcon icon="icon loading" />
|
|
||||||
{/if}
|
|
||||||
</svelte:fragment>
|
|
||||||
|
|
||||||
<HorizontalSplitter initialValue="70%">
|
|
||||||
<div class="content" slot="1">
|
|
||||||
<ImportExportConfigurator {uploadedFile} {openedFile} {previewReaderStore} />
|
|
||||||
|
|
||||||
{#if busy}
|
|
||||||
<LoadingInfo wrapper message="Processing import/export ..." />
|
|
||||||
{/if}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<svelte:fragment slot="2">
|
|
||||||
<WidgetColumnBar>
|
|
||||||
<WidgetColumnBarItem title="Output files" name="output" height="20%">
|
|
||||||
<RunnerOutputFiles {runnerId} {executeNumber} />
|
|
||||||
</WidgetColumnBarItem>
|
|
||||||
<WidgetColumnBarItem title="Messages" name="messages">
|
|
||||||
<SocketMessageView
|
|
||||||
eventName={runnerId ? `runner-info-${runnerId}` : null}
|
|
||||||
{executeNumber}
|
|
||||||
showNoMessagesAlert
|
|
||||||
/>
|
|
||||||
</WidgetColumnBarItem>
|
|
||||||
<WidgetColumnBarItem title="Preview" name="preview" skip={!$previewReaderStore}>
|
|
||||||
<PreviewDataGrid reader={$previewReaderStore} />
|
|
||||||
</WidgetColumnBarItem>
|
|
||||||
<WidgetColumnBarItem title="Advanced configuration" name="config" collapsed>
|
|
||||||
<FormTextField label="Schedule" name="schedule" />
|
|
||||||
<FormTextField label="Start variable index" name="startVariableIndex" />
|
|
||||||
</WidgetColumnBarItem>
|
|
||||||
</WidgetColumnBar>
|
|
||||||
</svelte:fragment>
|
|
||||||
</HorizontalSplitter>
|
|
||||||
|
|
||||||
<svelte:fragment slot="footer">
|
|
||||||
<div class="flex m-2">
|
|
||||||
{#if busy}
|
|
||||||
<LargeButton icon="icon stop" on:click={handleCancel}>Stop</LargeButton>
|
|
||||||
{:else}
|
|
||||||
<LargeFormButton on:click={handleExecute} icon="icon run">Run</LargeFormButton>
|
|
||||||
{/if}
|
|
||||||
<LargeFormButton icon="img sql-file" on:click={handleGenerateScript}>Generate script</LargeFormButton>
|
|
||||||
|
|
||||||
<LargeButton on:click={closeCurrentModal} icon="icon close">Close</LargeButton>
|
|
||||||
</div>
|
|
||||||
</svelte:fragment>
|
|
||||||
</ModalBase>
|
|
||||||
</FormProvider>
|
|
||||||
|
|
||||||
<style>
|
|
||||||
.content {
|
|
||||||
flex: 1;
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
overflow-y: auto;
|
|
||||||
overflow-x: hidden;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
@@ -13,7 +13,33 @@ const jsonFormat = {
|
|||||||
storageType: 'json',
|
storageType: 'json',
|
||||||
extension: 'json',
|
extension: 'json',
|
||||||
name: 'JSON',
|
name: 'JSON',
|
||||||
writerFunc: 'jsonArrayWriter',
|
readerFunc: 'jsonReader',
|
||||||
|
writerFunc: 'jsonWriter',
|
||||||
|
|
||||||
|
args: [
|
||||||
|
{
|
||||||
|
type: 'select',
|
||||||
|
name: 'jsonStyle',
|
||||||
|
label: 'JSON style',
|
||||||
|
options: [
|
||||||
|
{ name: 'Array', value: '' },
|
||||||
|
{ name: 'Object', value: 'object' },
|
||||||
|
],
|
||||||
|
apiName: 'jsonStyle',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 'text',
|
||||||
|
name: 'keyField',
|
||||||
|
label: 'Key field (only for "Object" style)',
|
||||||
|
apiName: 'keyField',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 'text',
|
||||||
|
name: 'rootField',
|
||||||
|
label: 'Root field',
|
||||||
|
apiName: 'rootField',
|
||||||
|
},
|
||||||
|
],
|
||||||
};
|
};
|
||||||
|
|
||||||
const sqlFormat = {
|
const sqlFormat = {
|
||||||
@@ -38,7 +64,7 @@ const jsonQuickExport = {
|
|||||||
label: 'JSON',
|
label: 'JSON',
|
||||||
extension: 'json',
|
extension: 'json',
|
||||||
createWriter: fileName => ({
|
createWriter: fileName => ({
|
||||||
functionName: 'jsonArrayWriter',
|
functionName: 'jsonWriter',
|
||||||
props: {
|
props: {
|
||||||
fileName,
|
fileName,
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -1,22 +1,39 @@
|
|||||||
|
<script lang="ts" context="module">
|
||||||
|
const getCurrentEditor = () => getActiveComponent('ImportExportTab');
|
||||||
|
|
||||||
|
registerFileCommands({
|
||||||
|
idPrefix: 'job',
|
||||||
|
category: 'Job',
|
||||||
|
getCurrentEditor,
|
||||||
|
folder: 'jobs',
|
||||||
|
format: 'json',
|
||||||
|
fileExtension: 'job',
|
||||||
|
|
||||||
|
// undoRedo: true,
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import moment from 'moment';
|
import moment from 'moment';
|
||||||
import { writable } from 'svelte/store';
|
import { writable } from 'svelte/store';
|
||||||
import HorizontalSplitter from '../elements/HorizontalSplitter.svelte';
|
import HorizontalSplitter from '../elements/HorizontalSplitter.svelte';
|
||||||
import LargeButton from '../buttons/LargeButton.svelte';
|
|
||||||
import LoadingInfo from '../elements/LoadingInfo.svelte';
|
import LoadingInfo from '../elements/LoadingInfo.svelte';
|
||||||
import VerticalSplitter from '../elements/VerticalSplitter.svelte';
|
|
||||||
|
|
||||||
import FormProvider from '../forms/FormProvider.svelte';
|
|
||||||
import FormTextField from '../forms/FormTextField.svelte';
|
import FormTextField from '../forms/FormTextField.svelte';
|
||||||
import LargeFormButton from '../forms/LargeFormButton.svelte';
|
|
||||||
import FontIcon from '../icons/FontIcon.svelte';
|
|
||||||
import createImpExpScript from '../impexp/createImpExpScript';
|
import createImpExpScript from '../impexp/createImpExpScript';
|
||||||
import ImportExportConfigurator from '../impexp/ImportExportConfigurator.svelte';
|
import ImportExportConfigurator from '../impexp/ImportExportConfigurator.svelte';
|
||||||
import PreviewDataGrid from '../impexp/PreviewDataGrid.svelte';
|
import PreviewDataGrid from '../impexp/PreviewDataGrid.svelte';
|
||||||
import { getDefaultFileFormat } from '../plugins/fileformats';
|
import { getDefaultFileFormat } from '../plugins/fileformats';
|
||||||
import RunnerOutputFiles from '../query/RunnerOutputFiles.svelte';
|
import RunnerOutputFiles from '../query/RunnerOutputFiles.svelte';
|
||||||
import SocketMessageView from '../query/SocketMessageView.svelte';
|
import SocketMessageView from '../query/SocketMessageView.svelte';
|
||||||
import { currentArchive, currentDatabase, extensions, visibleWidgetSideBar, selectedWidget } from '../stores';
|
import {
|
||||||
|
currentArchive,
|
||||||
|
currentDatabase,
|
||||||
|
extensions,
|
||||||
|
visibleWidgetSideBar,
|
||||||
|
selectedWidget,
|
||||||
|
activeTabId,
|
||||||
|
} from '../stores';
|
||||||
import { apiCall, apiOff, apiOn } from '../utility/api';
|
import { apiCall, apiOff, apiOn } from '../utility/api';
|
||||||
import createRef from '../utility/createRef';
|
import createRef from '../utility/createRef';
|
||||||
import openNewTab from '../utility/openNewTab';
|
import openNewTab from '../utility/openNewTab';
|
||||||
@@ -24,6 +41,15 @@
|
|||||||
import WidgetColumnBar from '../widgets/WidgetColumnBar.svelte';
|
import WidgetColumnBar from '../widgets/WidgetColumnBar.svelte';
|
||||||
import WidgetColumnBarItem from '../widgets/WidgetColumnBarItem.svelte';
|
import WidgetColumnBarItem from '../widgets/WidgetColumnBarItem.svelte';
|
||||||
import useEditorData from '../query/useEditorData';
|
import useEditorData from '../query/useEditorData';
|
||||||
|
import ToolStripContainer from '../buttons/ToolStripContainer.svelte';
|
||||||
|
import ToolStripButton from '../buttons/ToolStripButton.svelte';
|
||||||
|
import FormProviderCore from '../forms/FormProviderCore.svelte';
|
||||||
|
import { changeTab } from '../utility/common';
|
||||||
|
import _ from 'lodash';
|
||||||
|
import createActivator, { getActiveComponent } from '../utility/createActivator';
|
||||||
|
import { registerFileCommands } from '../commands/stdCommands';
|
||||||
|
import ToolStripCommandButton from '../buttons/ToolStripCommandButton.svelte';
|
||||||
|
import ToolStripSaveButton from '../buttons/ToolStripSaveButton.svelte';
|
||||||
|
|
||||||
let busy = false;
|
let busy = false;
|
||||||
let executeNumber = 0;
|
let executeNumber = 0;
|
||||||
@@ -32,17 +58,57 @@
|
|||||||
const previewReaderStore = writable(null);
|
const previewReaderStore = writable(null);
|
||||||
|
|
||||||
export let tabid;
|
export let tabid;
|
||||||
export let initialValues;
|
|
||||||
export let uploadedFile = undefined;
|
export let uploadedFile = undefined;
|
||||||
export let openedFile = undefined;
|
export let openedFile = undefined;
|
||||||
export let importToCurrentTarget = false;
|
export let importToCurrentTarget = false;
|
||||||
|
|
||||||
const refreshArchiveFolderRef = createRef(null);
|
const refreshArchiveFolderRef = createRef(null);
|
||||||
|
|
||||||
|
const formValues = writable({});
|
||||||
|
|
||||||
|
let domConfigurator;
|
||||||
|
|
||||||
|
export const activator = createActivator('ImportExportTab', true);
|
||||||
|
|
||||||
|
// const formValues = writable({
|
||||||
|
// sourceStorageType: 'database',
|
||||||
|
// targetStorageType: getDefaultFileFormat($extensions).storageType,
|
||||||
|
// targetArchiveFolder: $currentArchive,
|
||||||
|
// sourceArchiveFolder: $currentArchive,
|
||||||
|
// ...detectCurrentTarget(),
|
||||||
|
// ...initialValues,
|
||||||
|
// });
|
||||||
|
|
||||||
const { editorState, editorValue, setEditorData } = useEditorData({
|
const { editorState, editorValue, setEditorData } = useEditorData({
|
||||||
tabid,
|
tabid,
|
||||||
|
onInitialData: value => {
|
||||||
|
$formValues = {
|
||||||
|
sourceStorageType: 'database',
|
||||||
|
targetStorageType: getDefaultFileFormat($extensions).storageType,
|
||||||
|
targetArchiveFolder: $currentArchive,
|
||||||
|
sourceArchiveFolder: $currentArchive,
|
||||||
|
...detectCurrentTarget(),
|
||||||
|
...value,
|
||||||
|
};
|
||||||
|
|
||||||
|
if (uploadedFile) {
|
||||||
|
domConfigurator.addUploadedFile(uploadedFile);
|
||||||
|
}
|
||||||
|
if (openedFile) {
|
||||||
|
domConfigurator.addUploadedFile(openedFile);
|
||||||
|
}
|
||||||
|
|
||||||
|
changeTab(tabid, tab => ({
|
||||||
|
...tab,
|
||||||
|
props: _.omit(tab.props, ['uploadedFile', 'openedFile', 'importToCurrentTarget']),
|
||||||
|
}));
|
||||||
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// $: console.log('formValues', $formValues);
|
||||||
|
|
||||||
|
$: setEditorData($formValues);
|
||||||
|
|
||||||
function detectCurrentTarget() {
|
function detectCurrentTarget() {
|
||||||
if (!importToCurrentTarget) return {};
|
if (!importToCurrentTarget) return {};
|
||||||
|
|
||||||
@@ -67,7 +133,7 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
$: effect = useEffect(() => registerRunnerDone(runnerId));
|
$: effectRunner = useEffect(() => registerRunnerDone(runnerId));
|
||||||
|
|
||||||
function registerRunnerDone(rid) {
|
function registerRunnerDone(rid) {
|
||||||
if (rid) {
|
if (rid) {
|
||||||
@@ -80,7 +146,7 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
$: $effect;
|
$: $effectRunner;
|
||||||
|
|
||||||
const handleRunnerDone = () => {
|
const handleRunnerDone = () => {
|
||||||
busy = false;
|
busy = false;
|
||||||
@@ -94,7 +160,8 @@
|
|||||||
};
|
};
|
||||||
|
|
||||||
const handleGenerateScript = async e => {
|
const handleGenerateScript = async e => {
|
||||||
const code = await createImpExpScript($extensions, e.detail, undefined, true);
|
const values = $formValues as any;
|
||||||
|
const code = await createImpExpScript($extensions, values, undefined, true);
|
||||||
openNewTab(
|
openNewTab(
|
||||||
{
|
{
|
||||||
title: 'Shell #',
|
title: 'Shell #',
|
||||||
@@ -107,7 +174,7 @@
|
|||||||
|
|
||||||
const handleExecute = async e => {
|
const handleExecute = async e => {
|
||||||
if (busy) return;
|
if (busy) return;
|
||||||
const values = e.detail;
|
const values = $formValues as any;
|
||||||
busy = true;
|
busy = true;
|
||||||
const script = await createImpExpScript($extensions, values);
|
const script = await createImpExpScript($extensions, values);
|
||||||
executeNumber += 1;
|
executeNumber += 1;
|
||||||
@@ -128,63 +195,60 @@
|
|||||||
runid: runnerId,
|
runid: runnerId,
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export function getData() {
|
||||||
|
return $editorState.value || '';
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<FormProvider
|
<ToolStripContainer>
|
||||||
initialValues={{
|
<FormProviderCore values={formValues}>
|
||||||
sourceStorageType: 'database',
|
<HorizontalSplitter initialValue="70%">
|
||||||
targetStorageType: getDefaultFileFormat($extensions).storageType,
|
<div class="content" slot="1">
|
||||||
targetArchiveFolder: $currentArchive,
|
<ImportExportConfigurator
|
||||||
sourceArchiveFolder: $currentArchive,
|
bind:this={domConfigurator}
|
||||||
...detectCurrentTarget(),
|
{previewReaderStore}
|
||||||
...initialValues,
|
isTabActive={tabid == $activeTabId}
|
||||||
}}
|
/>
|
||||||
>
|
|
||||||
<HorizontalSplitter initialValue="70%">
|
|
||||||
<div class="content" slot="1">
|
|
||||||
<ImportExportConfigurator {uploadedFile} {openedFile} {previewReaderStore} />
|
|
||||||
|
|
||||||
{#if busy}
|
{#if busy}
|
||||||
<LoadingInfo wrapper message="Processing import/export ..." />
|
<LoadingInfo wrapper message="Processing import/export ..." />
|
||||||
{/if}
|
{/if}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<svelte:fragment slot="2">
|
<svelte:fragment slot="2">
|
||||||
<WidgetColumnBar>
|
<WidgetColumnBar>
|
||||||
<WidgetColumnBarItem title="Output files" name="output" height="20%">
|
<WidgetColumnBarItem title="Output files" name="output" height="20%">
|
||||||
<RunnerOutputFiles {runnerId} {executeNumber} />
|
<RunnerOutputFiles {runnerId} {executeNumber} />
|
||||||
</WidgetColumnBarItem>
|
</WidgetColumnBarItem>
|
||||||
<WidgetColumnBarItem title="Messages" name="messages">
|
<WidgetColumnBarItem title="Messages" name="messages">
|
||||||
<SocketMessageView
|
<SocketMessageView
|
||||||
eventName={runnerId ? `runner-info-${runnerId}` : null}
|
eventName={runnerId ? `runner-info-${runnerId}` : null}
|
||||||
{executeNumber}
|
{executeNumber}
|
||||||
showNoMessagesAlert
|
showNoMessagesAlert
|
||||||
/>
|
/>
|
||||||
</WidgetColumnBarItem>
|
</WidgetColumnBarItem>
|
||||||
<WidgetColumnBarItem title="Preview" name="preview" skip={!$previewReaderStore}>
|
<WidgetColumnBarItem title="Preview" name="preview" skip={!$previewReaderStore}>
|
||||||
<PreviewDataGrid reader={$previewReaderStore} />
|
<PreviewDataGrid reader={$previewReaderStore} />
|
||||||
</WidgetColumnBarItem>
|
</WidgetColumnBarItem>
|
||||||
<WidgetColumnBarItem title="Advanced configuration" name="config" collapsed>
|
<WidgetColumnBarItem title="Advanced configuration" name="config" collapsed>
|
||||||
<FormTextField label="Schedule" name="schedule" />
|
<FormTextField label="Schedule" name="schedule" />
|
||||||
<FormTextField label="Start variable index" name="startVariableIndex" />
|
<FormTextField label="Start variable index" name="startVariableIndex" />
|
||||||
</WidgetColumnBarItem>
|
</WidgetColumnBarItem>
|
||||||
</WidgetColumnBar>
|
</WidgetColumnBar>
|
||||||
</svelte:fragment>
|
</svelte:fragment>
|
||||||
</HorizontalSplitter>
|
</HorizontalSplitter>
|
||||||
|
</FormProviderCore>
|
||||||
<!-- <svelte:fragment slot="footer">
|
<svelte:fragment slot="toolstrip">
|
||||||
<div class="flex m-2">
|
{#if busy}
|
||||||
{#if busy}
|
<ToolStripButton icon="icon stop" on:click={handleCancel}>Stop</ToolStripButton>
|
||||||
<LargeButton icon="icon stop" on:click={handleCancel}>Stop</LargeButton>
|
{:else}
|
||||||
{:else}
|
<ToolStripButton on:click={handleExecute} icon="icon run">Run</ToolStripButton>
|
||||||
<LargeFormButton on:click={handleExecute} icon="icon run">Run</LargeFormButton>
|
{/if}
|
||||||
{/if}
|
<ToolStripButton icon="img sql-file" on:click={handleGenerateScript}>Generate script</ToolStripButton>
|
||||||
<LargeFormButton icon="img sql-file" on:click={handleGenerateScript}>Generate script</LargeFormButton>
|
<ToolStripSaveButton idPrefix="job" />
|
||||||
|
</svelte:fragment>
|
||||||
<LargeButton on:click={closeCurrentModal} icon="icon close">Close</LargeButton>
|
</ToolStripContainer>
|
||||||
</div>
|
|
||||||
</svelte:fragment> -->
|
|
||||||
</FormProvider>
|
|
||||||
|
|
||||||
<style>
|
<style>
|
||||||
.content {
|
.content {
|
||||||
@@ -193,5 +257,6 @@
|
|||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
overflow-y: auto;
|
overflow-y: auto;
|
||||||
overflow-x: hidden;
|
overflow-x: hidden;
|
||||||
|
background-color: var(--theme-bg-0);
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@@ -23,15 +23,6 @@
|
|||||||
onClick: () => getCurrentEditor().copyNodeScript(),
|
onClick: () => getCurrentEditor().copyNodeScript(),
|
||||||
});
|
});
|
||||||
|
|
||||||
registerCommand({
|
|
||||||
id: 'shell.openWizard',
|
|
||||||
category: 'Shell',
|
|
||||||
name: 'Open wizard',
|
|
||||||
// testEnabled: () => getCurrentEditor()?.openWizardEnabled(),
|
|
||||||
onClick: () => getCurrentEditor().openWizard(),
|
|
||||||
});
|
|
||||||
|
|
||||||
const configRegex = /\s*\/\/\s*@ImportExportConfigurator\s*\n\s*\/\/\s*(\{[^\n]+\})\n/;
|
|
||||||
const requireRegex = /\s*(\/\/\s*@require\s+[^\n]+)\n/g;
|
const requireRegex = /\s*(\/\/\s*@require\s+[^\n]+)\n/g;
|
||||||
const initRegex = /([^\n]+\/\/\s*@init)/g;
|
const initRegex = /([^\n]+\/\/\s*@init)/g;
|
||||||
</script>
|
</script>
|
||||||
@@ -47,8 +38,6 @@
|
|||||||
import { registerFileCommands } from '../commands/stdCommands';
|
import { registerFileCommands } from '../commands/stdCommands';
|
||||||
|
|
||||||
import VerticalSplitter from '../elements/VerticalSplitter.svelte';
|
import VerticalSplitter from '../elements/VerticalSplitter.svelte';
|
||||||
import ImportExportModal from '../modals/ImportExportModal.svelte';
|
|
||||||
import { showModal } from '../modals/modalTools';
|
|
||||||
import AceEditor from '../query/AceEditor.svelte';
|
import AceEditor from '../query/AceEditor.svelte';
|
||||||
import RunnerOutputPane from '../query/RunnerOutputPane.svelte';
|
import RunnerOutputPane from '../query/RunnerOutputPane.svelte';
|
||||||
import useEditorData from '../query/useEditorData';
|
import useEditorData from '../query/useEditorData';
|
||||||
@@ -60,7 +49,7 @@
|
|||||||
import { showSnackbarError } from '../utility/snackbar';
|
import { showSnackbarError } from '../utility/snackbar';
|
||||||
import useEffect from '../utility/useEffect';
|
import useEffect from '../utility/useEffect';
|
||||||
import useTimerLabel from '../utility/useTimerLabel';
|
import useTimerLabel from '../utility/useTimerLabel';
|
||||||
|
|
||||||
export let tabid;
|
export let tabid;
|
||||||
|
|
||||||
const tabVisible: any = getContext('tabVisible');
|
const tabVisible: any = getContext('tabVisible');
|
||||||
@@ -149,19 +138,6 @@
|
|||||||
copyTextToClipboard(resp);
|
copyTextToClipboard(resp);
|
||||||
}
|
}
|
||||||
|
|
||||||
// export function openWizardEnabled() {
|
|
||||||
// return ($editorValue || '').match(configRegex);
|
|
||||||
// }
|
|
||||||
|
|
||||||
export function openWizard() {
|
|
||||||
const jsonTextMatch = ($editorValue || '').match(configRegex);
|
|
||||||
if (jsonTextMatch) {
|
|
||||||
showModal(ImportExportModal, { initialValues: JSON.parse(jsonTextMatch[1]) });
|
|
||||||
} else {
|
|
||||||
showSnackbarError('No wizard info found');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function getActiveScript() {
|
function getActiveScript() {
|
||||||
const selectedText = domEditor.getEditor().getSelectedText();
|
const selectedText = domEditor.getEditor().getSelectedText();
|
||||||
const editorText = $editorValue;
|
const editorText = $editorValue;
|
||||||
@@ -208,7 +184,6 @@
|
|||||||
return [
|
return [
|
||||||
{ command: 'shell.execute' },
|
{ command: 'shell.execute' },
|
||||||
{ command: 'shell.kill' },
|
{ command: 'shell.kill' },
|
||||||
{ command: 'shell.openWizard' },
|
|
||||||
{ divider: true },
|
{ divider: true },
|
||||||
{ command: 'shell.toggleComment' },
|
{ command: 'shell.toggleComment' },
|
||||||
{ divider: true },
|
{ divider: true },
|
||||||
|
|||||||
@@ -42,8 +42,8 @@ export async function exportSqlDump(outputFile, connection, databaseName, pureFi
|
|||||||
onOpenResult:
|
onOpenResult:
|
||||||
pureFileName && !getElectron()
|
pureFileName && !getElectron()
|
||||||
? () => {
|
? () => {
|
||||||
downloadFromApi(`uploads/get?file=${pureFileName}`, 'file.sql');
|
downloadFromApi(`uploads/get?file=${pureFileName}`, 'file.sql');
|
||||||
}
|
}
|
||||||
: null,
|
: null,
|
||||||
openResultLabel: 'Download SQL file',
|
openResultLabel: 'Download SQL file',
|
||||||
});
|
});
|
||||||
@@ -226,17 +226,18 @@ export async function downloadFromApi(route: string, donloadName: string) {
|
|||||||
method: 'GET',
|
method: 'GET',
|
||||||
headers: resolveApiHeaders(),
|
headers: resolveApiHeaders(),
|
||||||
})
|
})
|
||||||
.then((res) => res.blob())
|
.then(res => res.blob())
|
||||||
.then((blob) => {
|
.then(blob => {
|
||||||
const objUrl = URL.createObjectURL(blob);
|
const objUrl = URL.createObjectURL(blob);
|
||||||
const a = document.createElement("a");
|
const a = document.createElement('a');
|
||||||
document.body.appendChild(a);
|
document.body.appendChild(a);
|
||||||
a.download = donloadName;
|
a.download = donloadName;
|
||||||
a.href = objUrl;
|
a.href = objUrl;
|
||||||
a.click();
|
a.click();
|
||||||
a.remove();
|
a.remove();
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
URL.revokeObjectURL(objUrl)
|
URL.revokeObjectURL(objUrl);
|
||||||
})
|
});
|
||||||
})
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
15
packages/web/src/utility/importExportTools.ts
Normal file
15
packages/web/src/utility/importExportTools.ts
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
import openNewTab from './openNewTab';
|
||||||
|
|
||||||
|
export function openImportExportTab(editorProps, additionalProps = {}) {
|
||||||
|
openNewTab(
|
||||||
|
{
|
||||||
|
tabComponent: 'ImportExportTab',
|
||||||
|
title: 'Import/Export',
|
||||||
|
icon: 'img export',
|
||||||
|
props: additionalProps,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
editor: editorProps,
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -1,17 +1,17 @@
|
|||||||
import { showModal } from '../modals/modalTools';
|
import { showModal } from '../modals/modalTools';
|
||||||
import { get } from 'svelte/store';
|
import { get } from 'svelte/store';
|
||||||
import newQuery from '../query/newQuery';
|
import newQuery from '../query/newQuery';
|
||||||
import ImportExportModal from '../modals/ImportExportModal.svelte';
|
|
||||||
import getElectron from './getElectron';
|
import getElectron from './getElectron';
|
||||||
import { currentDatabase, extensions, getCurrentDatabase } from '../stores';
|
import { currentDatabase, extensions, getCurrentDatabase } from '../stores';
|
||||||
import { getUploadListener } from './uploadFiles';
|
import { getUploadListener } from './uploadFiles';
|
||||||
import {getConnectionLabel, getDatabaseFileLabel } from 'dbgate-tools';
|
import { getConnectionLabel, getDatabaseFileLabel } from 'dbgate-tools';
|
||||||
import { apiCall } from './api';
|
import { apiCall } from './api';
|
||||||
import openNewTab from './openNewTab';
|
import openNewTab from './openNewTab';
|
||||||
import { openJsonDocument } from '../tabs/JsonTab.svelte';
|
import { openJsonDocument } from '../tabs/JsonTab.svelte';
|
||||||
import { SAVED_FILE_HANDLERS } from '../appobj/SavedFileAppObject.svelte';
|
import { SAVED_FILE_HANDLERS } from '../appobj/SavedFileAppObject.svelte';
|
||||||
import _ from 'lodash';
|
import _ from 'lodash';
|
||||||
import ErrorMessageModal from '../modals/ErrorMessageModal.svelte';
|
import ErrorMessageModal from '../modals/ErrorMessageModal.svelte';
|
||||||
|
import { openImportExportTab } from './importExportTools';
|
||||||
|
|
||||||
export function canOpenByElectron(file, extensions) {
|
export function canOpenByElectron(file, extensions) {
|
||||||
if (!file) return false;
|
if (!file) return false;
|
||||||
@@ -178,17 +178,30 @@ export function openElectronFileCore(filePath, extensions) {
|
|||||||
shortName: parsed.name,
|
shortName: parsed.name,
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
showModal(ImportExportModal, {
|
openImportExportTab(
|
||||||
openedFile: {
|
{
|
||||||
filePath,
|
|
||||||
storageType: format.storageType,
|
|
||||||
shortName: parsed.name,
|
|
||||||
},
|
|
||||||
importToCurrentTarget: true,
|
|
||||||
initialValues: {
|
|
||||||
sourceStorageType: format.storageType,
|
sourceStorageType: format.storageType,
|
||||||
},
|
},
|
||||||
});
|
{
|
||||||
|
openedFile: {
|
||||||
|
filePath,
|
||||||
|
storageType: format.storageType,
|
||||||
|
shortName: parsed.name,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
// showModal(ImportExportModal, {
|
||||||
|
// openedFile: {
|
||||||
|
// filePath,
|
||||||
|
// storageType: format.storageType,
|
||||||
|
// shortName: parsed.name,
|
||||||
|
// },
|
||||||
|
// importToCurrentTarget: true,
|
||||||
|
// initialValues: {
|
||||||
|
// sourceStorageType: format.storageType,
|
||||||
|
// },
|
||||||
|
// });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,9 +5,9 @@ import getElectron from './getElectron';
|
|||||||
import resolveApi, { resolveApiHeaders } from './resolveApi';
|
import resolveApi, { resolveApiHeaders } from './resolveApi';
|
||||||
import { findFileFormat } from '../plugins/fileformats';
|
import { findFileFormat } from '../plugins/fileformats';
|
||||||
import { showModal } from '../modals/modalTools';
|
import { showModal } from '../modals/modalTools';
|
||||||
import ImportExportModal from '../modals/ImportExportModal.svelte';
|
|
||||||
import ErrorMessageModal from '../modals/ErrorMessageModal.svelte';
|
import ErrorMessageModal from '../modals/ErrorMessageModal.svelte';
|
||||||
import openNewTab from './openNewTab';
|
import openNewTab from './openNewTab';
|
||||||
|
import { openImportExportTab } from './importExportTools';
|
||||||
|
|
||||||
let uploadListener;
|
let uploadListener;
|
||||||
|
|
||||||
@@ -79,13 +79,23 @@ export default function uploadFiles(files) {
|
|||||||
uploadListener(fileData);
|
uploadListener(fileData);
|
||||||
} else {
|
} else {
|
||||||
if (findFileFormat(ext, fileData.storageType)) {
|
if (findFileFormat(ext, fileData.storageType)) {
|
||||||
showModal(ImportExportModal, {
|
openImportExportTab(
|
||||||
uploadedFile: fileData,
|
{
|
||||||
importToCurrentTarget: true,
|
|
||||||
initialValues: {
|
|
||||||
sourceStorageType: fileData.storageType,
|
sourceStorageType: fileData.storageType,
|
||||||
},
|
},
|
||||||
});
|
{
|
||||||
|
uploadedFile: fileData,
|
||||||
|
importToCurrentTarget: true,
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
// showModal(ImportExportModal, {
|
||||||
|
// uploadedFile: fileData,
|
||||||
|
// importToCurrentTarget: true,
|
||||||
|
// initialValues: {
|
||||||
|
// sourceStorageType: fileData.storageType,
|
||||||
|
// },
|
||||||
|
// });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -20,6 +20,7 @@
|
|||||||
const queryFiles = useFiles({ folder: 'query' });
|
const queryFiles = useFiles({ folder: 'query' });
|
||||||
const sqliteFiles = useFiles({ folder: 'sqlite' });
|
const sqliteFiles = useFiles({ folder: 'sqlite' });
|
||||||
const diagramFiles = useFiles({ folder: 'diagrams' });
|
const diagramFiles = useFiles({ folder: 'diagrams' });
|
||||||
|
const jobFiles = useFiles({ folder: 'jobs' });
|
||||||
const perspectiveFiles = useFiles({ folder: 'perspectives' });
|
const perspectiveFiles = useFiles({ folder: 'perspectives' });
|
||||||
|
|
||||||
$: files = [
|
$: files = [
|
||||||
@@ -31,11 +32,12 @@
|
|||||||
...($sqliteFiles || []),
|
...($sqliteFiles || []),
|
||||||
...($diagramFiles || []),
|
...($diagramFiles || []),
|
||||||
...($perspectiveFiles || []),
|
...($perspectiveFiles || []),
|
||||||
|
...($jobFiles || []),
|
||||||
];
|
];
|
||||||
|
|
||||||
function handleRefreshFiles() {
|
function handleRefreshFiles() {
|
||||||
apiCall('files/refresh', {
|
apiCall('files/refresh', {
|
||||||
folders: ['sql', 'shell', 'markdown', 'charts', 'query', 'sqlite', 'diagrams', 'perspectives'],
|
folders: ['sql', 'shell', 'markdown', 'charts', 'query', 'sqlite', 'diagrams', 'perspectives', 'jobs'],
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@@ -32,10 +32,11 @@
|
|||||||
"prepublishOnly": "yarn build"
|
"prepublishOnly": "yarn build"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"csv": "^5.3.2",
|
"csv": "^6.3.10",
|
||||||
"dbgate-plugin-tools": "^1.0.7",
|
"dbgate-plugin-tools": "^1.0.7",
|
||||||
|
"line-reader": "^0.4.0",
|
||||||
"lodash": "^4.17.21",
|
"lodash": "^4.17.21",
|
||||||
"webpack": "^5.91.0",
|
"webpack": "^5.91.0",
|
||||||
"webpack-cli": "^5.1.4"
|
"webpack-cli": "^5.1.4"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,8 +2,32 @@ const zipObject = require('lodash/zipObject');
|
|||||||
const csv = require('csv');
|
const csv = require('csv');
|
||||||
const fs = require('fs');
|
const fs = require('fs');
|
||||||
const stream = require('stream');
|
const stream = require('stream');
|
||||||
|
const lineReader = require('line-reader');
|
||||||
|
|
||||||
let dbgateApi;
|
let dbgateApi;
|
||||||
|
|
||||||
|
function readFirstLine(file) {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
lineReader.open(file, (err, reader) => {
|
||||||
|
if (err) {
|
||||||
|
reject(err);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (reader.hasNextLine()) {
|
||||||
|
reader.nextLine((err, line) => {
|
||||||
|
if (err) {
|
||||||
|
reader.close(() => reject(err)); // Ensure reader is closed on error
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
reader.close(() => resolve(line)); // Ensure reader is closed after reading
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
reader.close(() => resolve(null)); // Properly close if no lines are present
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
class CsvPrepareStream extends stream.Transform {
|
class CsvPrepareStream extends stream.Transform {
|
||||||
constructor({ header }) {
|
constructor({ header }) {
|
||||||
super({ objectMode: true });
|
super({ objectMode: true });
|
||||||
@@ -46,13 +70,29 @@ class CsvPrepareStream extends stream.Transform {
|
|||||||
|
|
||||||
async function reader({ fileName, encoding = 'utf-8', header = true, delimiter, limitRows = undefined }) {
|
async function reader({ fileName, encoding = 'utf-8', header = true, delimiter, limitRows = undefined }) {
|
||||||
console.log(`Reading file ${fileName}`);
|
console.log(`Reading file ${fileName}`);
|
||||||
|
const downloadedFile = await dbgateApi.download(fileName);
|
||||||
|
|
||||||
|
if (!delimiter) {
|
||||||
|
// auto detect delimiter
|
||||||
|
// read first line from downloadedFile
|
||||||
|
const firstLine = await readFirstLine(downloadedFile);
|
||||||
|
if (firstLine) {
|
||||||
|
const delimiterCounts = {
|
||||||
|
',': firstLine.replace(/[^,]/g, '').length,
|
||||||
|
';': firstLine.replace(/[^;]/g, '').length,
|
||||||
|
'|': firstLine.replace(/[^|]/g, '').length,
|
||||||
|
};
|
||||||
|
|
||||||
|
delimiter = Object.keys(delimiterCounts).reduce((a, b) => (delimiterCounts[a] > delimiterCounts[b] ? a : b), ',');
|
||||||
|
}
|
||||||
|
}
|
||||||
const csvStream = csv.parse({
|
const csvStream = csv.parse({
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
delimiter,
|
delimiter,
|
||||||
skip_lines_with_error: true,
|
skip_lines_with_error: true,
|
||||||
to_line: limitRows ? limitRows + 1 : undefined,
|
to_line: limitRows ? limitRows + 1 : undefined,
|
||||||
|
ltrim: true,
|
||||||
});
|
});
|
||||||
const downloadedFile = await dbgateApi.download(fileName);
|
|
||||||
const fileStream = fs.createReadStream(downloadedFile, encoding);
|
const fileStream = fs.createReadStream(downloadedFile, encoding);
|
||||||
const csvPrepare = new CsvPrepareStream({ header });
|
const csvPrepare = new CsvPrepareStream({ header });
|
||||||
fileStream.pipe(csvStream);
|
fileStream.pipe(csvStream);
|
||||||
|
|||||||
@@ -17,6 +17,7 @@ const fileFormat = {
|
|||||||
name: 'delimiter',
|
name: 'delimiter',
|
||||||
label: 'Delimiter',
|
label: 'Delimiter',
|
||||||
options: [
|
options: [
|
||||||
|
{ name: 'Auto-detect', value: '' },
|
||||||
{ name: 'Comma (,)', value: ',' },
|
{ name: 'Comma (,)', value: ',' },
|
||||||
{ name: 'Semicolon (;)', value: ';' },
|
{ name: 'Semicolon (;)', value: ';' },
|
||||||
{ name: 'Tab', value: '\t' },
|
{ name: 'Tab', value: '\t' },
|
||||||
|
|||||||
70
yarn.lock
70
yarn.lock
@@ -3116,30 +3116,30 @@ cssstyle@^2.3.0:
|
|||||||
dependencies:
|
dependencies:
|
||||||
cssom "~0.3.6"
|
cssom "~0.3.6"
|
||||||
|
|
||||||
csv-generate@^3.4.3:
|
csv-generate@^4.4.1:
|
||||||
version "3.4.3"
|
version "4.4.1"
|
||||||
resolved "https://registry.yarnpkg.com/csv-generate/-/csv-generate-3.4.3.tgz#bc42d943b45aea52afa896874291da4b9108ffff"
|
resolved "https://registry.yarnpkg.com/csv-generate/-/csv-generate-4.4.1.tgz#729781ace8d1b92f6bfb407d1ab9548728c55681"
|
||||||
integrity sha512-w/T+rqR0vwvHqWs/1ZyMDWtHHSJaN06klRqJXBEpDJaM/+dZkso0OKh1VcuuYvK3XM53KysVNq8Ko/epCK8wOw==
|
integrity sha512-O/einO0v4zPmXaOV+sYqGa02VkST4GP5GLpWBNHEouIU7pF3kpGf3D0kCCvX82ydIY4EKkOK+R8b1BYsRXravg==
|
||||||
|
|
||||||
csv-parse@^4.16.3:
|
csv-parse@^5.5.6:
|
||||||
version "4.16.3"
|
version "5.5.6"
|
||||||
resolved "https://registry.yarnpkg.com/csv-parse/-/csv-parse-4.16.3.tgz#7ca624d517212ebc520a36873c3478fa66efbaf7"
|
resolved "https://registry.yarnpkg.com/csv-parse/-/csv-parse-5.5.6.tgz#0d726d58a60416361358eec291a9f93abe0b6b1a"
|
||||||
integrity sha512-cO1I/zmz4w2dcKHVvpCr7JVRu8/FymG5OEpmvsZYlccYolPBLoVGKUHgNoc4ZGkFeFlWGEDmMyBM+TTqRdW/wg==
|
integrity sha512-uNpm30m/AGSkLxxy7d9yRXpJQFrZzVWLFBkS+6ngPcZkw/5k3L/jjFuj7tVnEpRn+QgmiXr21nDlhCiUK4ij2A==
|
||||||
|
|
||||||
csv-stringify@^5.6.5:
|
csv-stringify@^6.5.1:
|
||||||
version "5.6.5"
|
version "6.5.1"
|
||||||
resolved "https://registry.yarnpkg.com/csv-stringify/-/csv-stringify-5.6.5.tgz#c6d74badda4b49a79bf4e72f91cce1e33b94de00"
|
resolved "https://registry.yarnpkg.com/csv-stringify/-/csv-stringify-6.5.1.tgz#a31837dd35e34787e3c248159c982a21af964d94"
|
||||||
integrity sha512-PjiQ659aQ+fUTQqSrd1XEDnOr52jh30RBurfzkscaE2tPaFsDH5wOAHJiw8XAHphRknCwMUE9KRayc4K/NbO8A==
|
integrity sha512-+9lpZfwpLntpTIEpFbwQyWuW/hmI/eHuJZD1XzeZpfZTqkf1fyvBbBLXTJJMsBuuS11uTShMqPwzx4A6ffXgRQ==
|
||||||
|
|
||||||
csv@^5.3.2:
|
csv@^6.3.10:
|
||||||
version "5.5.3"
|
version "6.3.10"
|
||||||
resolved "https://registry.yarnpkg.com/csv/-/csv-5.5.3.tgz#cd26c1e45eae00ce6a9b7b27dcb94955ec95207d"
|
resolved "https://registry.yarnpkg.com/csv/-/csv-6.3.10.tgz#960a3a9cef08573ecca2d80ddb71152aca383088"
|
||||||
integrity sha512-QTaY0XjjhTQOdguARF0lGKm5/mEq9PD9/VhZZegHDIBq2tQwgNpHc3dneD4mGo2iJs+fTKv5Bp0fZ+BRuY3Z0g==
|
integrity sha512-5NYZG4AN2ZUthmNxIudgBEdMPUnbQHu9V4QTzBPqQzUP3KQsFiJo+8HQ0+oVxj1PomIT1/f67VI1QH/hsrZLKA==
|
||||||
dependencies:
|
dependencies:
|
||||||
csv-generate "^3.4.3"
|
csv-generate "^4.4.1"
|
||||||
csv-parse "^4.16.3"
|
csv-parse "^5.5.6"
|
||||||
csv-stringify "^5.6.5"
|
csv-stringify "^6.5.1"
|
||||||
stream-transform "^2.1.3"
|
stream-transform "^3.3.2"
|
||||||
|
|
||||||
dashdash@^1.12.0:
|
dashdash@^1.12.0:
|
||||||
version "1.14.1"
|
version "1.14.1"
|
||||||
@@ -7456,11 +7456,6 @@ mixin-deep@^1.2.0:
|
|||||||
for-in "^1.0.2"
|
for-in "^1.0.2"
|
||||||
is-extendable "^1.0.1"
|
is-extendable "^1.0.1"
|
||||||
|
|
||||||
mixme@^0.5.1:
|
|
||||||
version "0.5.10"
|
|
||||||
resolved "https://registry.yarnpkg.com/mixme/-/mixme-0.5.10.tgz#d653b2984b75d9018828f1ea333e51717ead5f51"
|
|
||||||
integrity sha512-5H76ANWinB1H3twpJ6JY8uvAtpmFvHNArpilJAjXRKXSDDLPIMoZArw5SH0q9z+lLs8IrMw7Q2VWpWimFKFT1Q==
|
|
||||||
|
|
||||||
mkdirp-classic@^0.5.2, mkdirp-classic@^0.5.3:
|
mkdirp-classic@^0.5.2, mkdirp-classic@^0.5.3:
|
||||||
version "0.5.3"
|
version "0.5.3"
|
||||||
resolved "https://registry.yarnpkg.com/mkdirp-classic/-/mkdirp-classic-0.5.3.tgz#fa10c9115cc6d8865be221ba47ee9bed78601113"
|
resolved "https://registry.yarnpkg.com/mkdirp-classic/-/mkdirp-classic-0.5.3.tgz#fa10c9115cc6d8865be221ba47ee9bed78601113"
|
||||||
@@ -9750,12 +9745,22 @@ stoppable@^1.1.0:
|
|||||||
resolved "https://registry.yarnpkg.com/stoppable/-/stoppable-1.1.0.tgz#32da568e83ea488b08e4d7ea2c3bcc9d75015d5b"
|
resolved "https://registry.yarnpkg.com/stoppable/-/stoppable-1.1.0.tgz#32da568e83ea488b08e4d7ea2c3bcc9d75015d5b"
|
||||||
integrity sha512-KXDYZ9dszj6bzvnEMRYvxgeTHU74QBFL54XKtP3nyMuJ81CFYtABZ3bAzL2EdFUaEwJOBOgENyFj3R7oTzDyyw==
|
integrity sha512-KXDYZ9dszj6bzvnEMRYvxgeTHU74QBFL54XKtP3nyMuJ81CFYtABZ3bAzL2EdFUaEwJOBOgENyFj3R7oTzDyyw==
|
||||||
|
|
||||||
stream-transform@^2.1.3:
|
stream-chain@^2.2.5:
|
||||||
version "2.1.3"
|
version "2.2.5"
|
||||||
resolved "https://registry.yarnpkg.com/stream-transform/-/stream-transform-2.1.3.tgz#a1c3ecd72ddbf500aa8d342b0b9df38f5aa598e3"
|
resolved "https://registry.yarnpkg.com/stream-chain/-/stream-chain-2.2.5.tgz#b30967e8f14ee033c5b9a19bbe8a2cba90ba0d09"
|
||||||
integrity sha512-9GHUiM5hMiCi6Y03jD2ARC1ettBXkQBoQAe7nJsPknnI0ow10aXjTnew8QtYQmLjzn974BnmWEAJgCY6ZP1DeQ==
|
integrity sha512-1TJmBx6aSWqZ4tx7aTpBDXK0/e2hhcNSTV8+CbFJtDjbb+I1mZ8lHit0Grw9GRT+6JbIrrDd8esncgBi8aBXGA==
|
||||||
|
|
||||||
|
stream-json@^1.8.0:
|
||||||
|
version "1.8.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/stream-json/-/stream-json-1.8.0.tgz#53f486b2e3b4496c506131f8d7260ba42def151c"
|
||||||
|
integrity sha512-HZfXngYHUAr1exT4fxlbc1IOce1RYxp2ldeaf97LYCOPSoOqY/1Psp7iGvpb+6JIOgkra9zDYnPX01hGAHzEPw==
|
||||||
dependencies:
|
dependencies:
|
||||||
mixme "^0.5.1"
|
stream-chain "^2.2.5"
|
||||||
|
|
||||||
|
stream-transform@^3.3.2:
|
||||||
|
version "3.3.2"
|
||||||
|
resolved "https://registry.yarnpkg.com/stream-transform/-/stream-transform-3.3.2.tgz#398c67b2f3b6ed5d04ceadde9e412bda8416c8ab"
|
||||||
|
integrity sha512-v64PUnPy9Qw94NGuaEMo+9RHQe4jTBYf+NkTtqkCgeuiNo8NlL0LtLR7fkKWNVFtp3RhIm5Dlxkgm5uz7TDimQ==
|
||||||
|
|
||||||
streamsearch@^1.1.0:
|
streamsearch@^1.1.0:
|
||||||
version "1.1.0"
|
version "1.1.0"
|
||||||
@@ -10182,6 +10187,11 @@ tmp@^0.0.33:
|
|||||||
dependencies:
|
dependencies:
|
||||||
os-tmpdir "~1.0.2"
|
os-tmpdir "~1.0.2"
|
||||||
|
|
||||||
|
tmp@^0.2.3:
|
||||||
|
version "0.2.3"
|
||||||
|
resolved "https://registry.yarnpkg.com/tmp/-/tmp-0.2.3.tgz#eb783cc22bc1e8bebd0671476d46ea4eb32a79ae"
|
||||||
|
integrity sha512-nZD7m9iCPC5g0pYmcaxogYKggSfLsdxl8of3Q/oIbqCqLLIO9IAF0GWjX1z9NZRHPiXv8Wex4yDCaZsgEw0Y8w==
|
||||||
|
|
||||||
tmpl@1.0.5:
|
tmpl@1.0.5:
|
||||||
version "1.0.5"
|
version "1.0.5"
|
||||||
resolved "https://registry.yarnpkg.com/tmpl/-/tmpl-1.0.5.tgz#8683e0b902bb9c20c4f726e3c0b69f36518c07cc"
|
resolved "https://registry.yarnpkg.com/tmpl/-/tmpl-1.0.5.tgz#8683e0b902bb9c20c4f726e3c0b69f36518c07cc"
|
||||||
|
|||||||
Reference in New Issue
Block a user