dbgate-api 6.1.6 → 6.2.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "dbgate-api",
3
3
  "main": "src/index.js",
4
- "version": "6.1.6",
4
+ "version": "6.2.1",
5
5
  "homepage": "https://dbgate.org/",
6
6
  "repository": {
7
7
  "type": "git",
@@ -29,10 +29,10 @@
29
29
  "compare-versions": "^3.6.0",
30
30
  "cors": "^2.8.5",
31
31
  "cross-env": "^6.0.3",
32
- "dbgate-datalib": "^6.1.6",
32
+ "dbgate-datalib": "^6.2.1",
33
33
  "dbgate-query-splitter": "^4.11.3",
34
- "dbgate-sqltree": "^6.1.6",
35
- "dbgate-tools": "^6.1.6",
34
+ "dbgate-sqltree": "^6.2.1",
35
+ "dbgate-tools": "^6.2.1",
36
36
  "debug": "^4.3.4",
37
37
  "diff": "^5.0.0",
38
38
  "diff2html": "^3.4.13",
@@ -74,6 +74,8 @@
74
74
  "start:storage": "env-cmd -f env/storage/.env node src/index.js --listen-api",
75
75
  "start:storage:built": "env-cmd -f env/storage/.env cross-env DEVMODE= BUILTWEBMODE=1 node dist/bundle.js --listen-api",
76
76
  "start:singleconn": "env-cmd node src/index.js --server localhost --user root --port 3307 --engine mysql@dbgate-plugin-mysql --password test --listen-api",
77
+ "start:azure": "env-cmd -f env/azure/.env node src/index.js --listen-api",
78
+ "start:e2e:team": "cross-env DEVWEB=1 DEVMODE=1 env-cmd -f ../../e2e-tests/env/team/.env node src/index.js --listen-api",
77
79
  "ts": "tsc",
78
80
  "build": "webpack",
79
81
  "build:doc": "jsdoc2md --template doctpl.hbs ./src/shell/* > ../../../dbgate.github.io/_docs/apidoc.md"
@@ -81,7 +83,7 @@
81
83
  "devDependencies": {
82
84
  "@types/fs-extra": "^9.0.11",
83
85
  "@types/lodash": "^4.14.149",
84
- "dbgate-types": "^6.1.6",
86
+ "dbgate-types": "^6.2.1",
85
87
  "env-cmd": "^10.1.0",
86
88
  "jsdoc-to-markdown": "^9.0.5",
87
89
  "node-loader": "^1.0.2",
@@ -136,7 +136,7 @@ module.exports = {
136
136
 
137
137
  deleteSettings_meta: true,
138
138
  async deleteSettings() {
139
- await fs.unlink(path.join(datadir(), 'settings.json'));
139
+ await fs.unlink(path.join(datadir(), processArgs.runE2eTests ? 'settings-e2etests.json' : 'settings.json'));
140
140
  return true;
141
141
  },
142
142
 
@@ -161,7 +161,10 @@ module.exports = {
161
161
 
162
162
  async loadSettings() {
163
163
  try {
164
- const settingsText = await fs.readFile(path.join(datadir(), 'settings.json'), { encoding: 'utf-8' });
164
+ const settingsText = await fs.readFile(
165
+ path.join(datadir(), processArgs.runE2eTests ? 'settings-e2etests.json' : 'settings.json'),
166
+ { encoding: 'utf-8' }
167
+ );
165
168
  return {
166
169
  ...this.fillMissingSettings(JSON.parse(settingsText)),
167
170
  'other.licenseKey': platformInfo.isElectron ? await this.loadLicenseKey() : undefined,
@@ -247,7 +250,10 @@ module.exports = {
247
250
  ...currentValue,
248
251
  ..._.omit(values, ['other.licenseKey']),
249
252
  };
250
- await fs.writeFile(path.join(datadir(), 'settings.json'), JSON.stringify(updated, undefined, 2));
253
+ await fs.writeFile(
254
+ path.join(datadir(), processArgs.runE2eTests ? 'settings-e2etests.json' : 'settings.json'),
255
+ JSON.stringify(updated, undefined, 2)
256
+ );
251
257
  // this.settingsValue = updated;
252
258
 
253
259
  if (currentValue['other.licenseKey'] != values['other.licenseKey']) {
@@ -34,6 +34,8 @@ const pipeForkLogs = require('../utility/pipeForkLogs');
34
34
  const crypto = require('crypto');
35
35
  const loadModelTransform = require('../utility/loadModelTransform');
36
36
  const exportDbModelSql = require('../utility/exportDbModelSql');
37
+ const axios = require('axios');
38
+ const { callTextToSqlApi, callCompleteOnCursorApi, callRefactorSqlQueryApi } = require('../utility/authProxy');
37
39
 
38
40
  const logger = getLogger('databaseConnections');
39
41
 
@@ -69,6 +71,11 @@ module.exports = {
69
71
  handle_error(conid, database, props) {
70
72
  const { error } = props;
71
73
  logger.error(`Error in database connection ${conid}, database ${database}: ${error}`);
74
+ if (props?.msgid) {
75
+ const [resolve, reject] = this.requests[props?.msgid];
76
+ reject(error);
77
+ delete this.requests[props?.msgid];
78
+ }
72
79
  },
73
80
  handle_response(conid, database, { msgid, ...response }) {
74
81
  const [resolve, reject] = this.requests[msgid];
@@ -253,9 +260,9 @@ module.exports = {
253
260
  },
254
261
 
255
262
  loadFieldValues_meta: true,
256
- async loadFieldValues({ conid, database, schemaName, pureName, field, search }, req) {
263
+ async loadFieldValues({ conid, database, schemaName, pureName, field, search, dataType }, req) {
257
264
  testConnectionPermission(conid, req);
258
- return this.loadDataCore('loadFieldValues', { conid, database, schemaName, pureName, field, search });
265
+ return this.loadDataCore('loadFieldValues', { conid, database, schemaName, pureName, field, search, dataType });
259
266
  },
260
267
 
261
268
  callMethod_meta: true,
@@ -401,6 +408,10 @@ module.exports = {
401
408
 
402
409
  structure_meta: true,
403
410
  async structure({ conid, database, modelTransFile = null }, req) {
411
+ if (!conid || !database) {
412
+ return {};
413
+ }
414
+
404
415
  testConnectionPermission(conid, req);
405
416
  if (conid == '__model') {
406
417
  const model = await importDbModel(database);
@@ -558,4 +569,47 @@ module.exports = {
558
569
 
559
570
  return true;
560
571
  },
572
+
573
+ textToSql_meta: true,
574
+ async textToSql({ conid, database, text, dialect }) {
575
+ const existing = this.opened.find(x => x.conid == conid && x.database == database);
576
+ const { structure } = existing || {};
577
+ if (!structure) return { errorMessage: 'No database structure' };
578
+
579
+ const res = await callTextToSqlApi(text, structure, dialect);
580
+
581
+ if (!res?.sql) {
582
+ return { errorMessage: 'No SQL generated' };
583
+ }
584
+
585
+ return res;
586
+ },
587
+
588
+ completeOnCursor_meta: true,
589
+ async completeOnCursor({ conid, database, text, dialect, line }) {
590
+ const existing = this.opened.find(x => x.conid == conid && x.database == database);
591
+ const { structure } = existing || {};
592
+ if (!structure) return { errorMessage: 'No database structure' };
593
+ const res = await callCompleteOnCursorApi(text, structure, dialect, line);
594
+
595
+ if (!res?.variants) {
596
+ return { errorMessage: 'No SQL generated' };
597
+ }
598
+
599
+ return res;
600
+ },
601
+
602
+ refactorSqlQuery_meta: true,
603
+ async refactorSqlQuery({ conid, database, query, task, dialect }) {
604
+ const existing = this.opened.find(x => x.conid == conid && x.database == database);
605
+ const { structure } = existing || {};
606
+ if (!structure) return { errorMessage: 'No database structure' };
607
+ const res = await callRefactorSqlQueryApi(query, task, structure, dialect);
608
+
609
+ if (!res?.sql) {
610
+ return { errorMessage: 'No SQL generated' };
611
+ }
612
+
613
+ return res;
614
+ },
561
615
  };
@@ -56,12 +56,19 @@ module.exports = {
56
56
  handle_done(sesid, props) {
57
57
  socket.emit(`session-done-${sesid}`);
58
58
  if (!props.skipFinishedMessage) {
59
- this.dispatchMessage(sesid, 'Query execution finished');
59
+ if (props.controlCommand) {
60
+ this.dispatchMessage(sesid, `${_.startCase(props.controlCommand)} finished`);
61
+ } else {
62
+ this.dispatchMessage(sesid, 'Query execution finished');
63
+ }
60
64
  }
61
65
  const session = this.opened.find(x => x.sesid == sesid);
62
66
  if (session.loadingReader_jslid) {
63
67
  socket.emit(`session-jslid-done-${session.loadingReader_jslid}`);
64
68
  }
69
+ if (props.autoCommit) {
70
+ this.executeControlCommand({ sesid, command: 'commitTransaction' });
71
+ }
65
72
  if (session.killOnDone) {
66
73
  this.kill({ sesid });
67
74
  }
@@ -131,7 +138,7 @@ module.exports = {
131
138
  },
132
139
 
133
140
  executeQuery_meta: true,
134
- async executeQuery({ sesid, sql }) {
141
+ async executeQuery({ sesid, sql, autoCommit }) {
135
142
  const session = this.opened.find(x => x.sesid == sesid);
136
143
  if (!session) {
137
144
  throw new Error('Invalid session');
@@ -139,7 +146,21 @@ module.exports = {
139
146
 
140
147
  logger.info({ sesid, sql }, 'Processing query');
141
148
  this.dispatchMessage(sesid, 'Query execution started');
142
- session.subprocess.send({ msgtype: 'executeQuery', sql });
149
+ session.subprocess.send({ msgtype: 'executeQuery', sql, autoCommit });
150
+
151
+ return { state: 'ok' };
152
+ },
153
+
154
+ executeControlCommand_meta: true,
155
+ async executeControlCommand({ sesid, command }) {
156
+ const session = this.opened.find(x => x.sesid == sesid);
157
+ if (!session) {
158
+ throw new Error('Invalid session');
159
+ }
160
+
161
+ logger.info({ sesid, command }, 'Processing control command');
162
+ this.dispatchMessage(sesid, `${_.startCase(command)} started`);
163
+ session.subprocess.send({ msgtype: 'executeControlCommand', command });
143
164
 
144
165
  return { state: 'ok' };
145
166
  },
@@ -1,5 +1,5 @@
1
1
 
2
2
  module.exports = {
3
- version: '6.1.6',
4
- buildTime: '2025-02-04T16:05:53.985Z'
3
+ version: '6.2.1',
4
+ buildTime: '2025-02-28T09:57:00.403Z'
5
5
  };
package/src/main.js CHANGED
@@ -78,6 +78,8 @@ function start() {
78
78
  app.use(getExpressPath('/'), express.static('/home/dbgate-docker/public'));
79
79
  } else if (platformInfo.isAwsUbuntuLayout) {
80
80
  app.use(getExpressPath('/'), express.static('/home/ubuntu/build/public'));
81
+ } else if (platformInfo.isAzureUbuntuLayout) {
82
+ app.use(getExpressPath('/'), express.static('/home/azureuser/build/public'));
81
83
  } else if (processArgs.runE2eTests) {
82
84
  app.use(getExpressPath('/'), express.static(path.resolve('packer/build/public')));
83
85
  } else if (platformInfo.isNpmDist) {
@@ -140,6 +142,10 @@ function start() {
140
142
  const port = process.env.PORT || 3000;
141
143
  logger.info(`DbGate API listening on port ${port} (AWS AMI build)`);
142
144
  server.listen(port);
145
+ } else if (platformInfo.isAzureUbuntuLayout) {
146
+ const port = process.env.PORT || 3000;
147
+ logger.info(`DbGate API listening on port ${port} (Azure VM build)`);
148
+ server.listen(port);
143
149
  } else if (platformInfo.isNpmDist) {
144
150
  getPort({
145
151
  port: parseInt(
@@ -1,6 +1,6 @@
1
1
  const childProcessChecker = require('../utility/childProcessChecker');
2
2
  const requireEngineDriver = require('../utility/requireEngineDriver');
3
- const connectUtility = require('../utility/connectUtility');
3
+ const { connectUtility } = require('../utility/connectUtility');
4
4
  const { handleProcessCommunication } = require('../utility/processComm');
5
5
  const { pickSafeConnectionInfo } = require('../utility/crypting');
6
6
  const _ = require('lodash');
@@ -11,7 +11,7 @@ const {
11
11
  extractErrorLogData,
12
12
  } = require('dbgate-tools');
13
13
  const requireEngineDriver = require('../utility/requireEngineDriver');
14
- const connectUtility = require('../utility/connectUtility');
14
+ const { connectUtility } = require('../utility/connectUtility');
15
15
  const { handleProcessCommunication } = require('../utility/processComm');
16
16
  const { SqlGenerator } = require('dbgate-tools');
17
17
  const generateDeploySql = require('../shell/generateDeploySql');
@@ -213,13 +213,12 @@ async function handleRunOperation({ msgid, operation, useTransaction }, skipRead
213
213
  }
214
214
  }
215
215
 
216
- async function handleQueryData({ msgid, sql }, skipReadonlyCheck = false) {
216
+ async function handleQueryData({ msgid, sql, range }, skipReadonlyCheck = false) {
217
217
  await waitConnected();
218
218
  const driver = requireEngineDriver(storedConnection);
219
219
  try {
220
220
  if (!skipReadonlyCheck) ensureExecuteCustomScript(driver);
221
- // console.log(sql);
222
- const res = await driver.query(dbhan, sql);
221
+ const res = await driver.query(dbhan, sql, { range });
223
222
  process.send({ msgtype: 'response', msgid, ...res });
224
223
  } catch (err) {
225
224
  process.send({
@@ -234,7 +233,7 @@ async function handleSqlSelect({ msgid, select }) {
234
233
  const driver = requireEngineDriver(storedConnection);
235
234
  const dmp = driver.createDumper();
236
235
  dumpSqlSelect(dmp, select);
237
- return handleQueryData({ msgid, sql: dmp.s }, true);
236
+ return handleQueryData({ msgid, sql: dmp.s, range: select.range }, true);
238
237
  }
239
238
 
240
239
  async function handleDriverDataCore(msgid, callMethod, { logName }) {
@@ -291,10 +290,14 @@ async function handleLoadKeyTableRange({ msgid, key, cursor, count }) {
291
290
  });
292
291
  }
293
292
 
294
- async function handleLoadFieldValues({ msgid, schemaName, pureName, field, search }) {
295
- return handleDriverDataCore(msgid, driver => driver.loadFieldValues(dbhan, { schemaName, pureName }, field, search), {
296
- logName: 'loadFieldValues',
297
- });
293
+ async function handleLoadFieldValues({ msgid, schemaName, pureName, field, search, dataType }) {
294
+ return handleDriverDataCore(
295
+ msgid,
296
+ driver => driver.loadFieldValues(dbhan, { schemaName, pureName }, field, search, dataType),
297
+ {
298
+ logName: 'loadFieldValues',
299
+ }
300
+ );
298
301
  }
299
302
 
300
303
  function ensureExecuteCustomScript(driver) {
@@ -336,6 +339,7 @@ async function handleSqlPreview({ msgid, objects, options }) {
336
339
  }, 500);
337
340
  }
338
341
  } catch (err) {
342
+ console.error(err);
339
343
  process.send({
340
344
  msgtype: 'response',
341
345
  msgid,
@@ -423,7 +427,11 @@ function start() {
423
427
  await handleMessage(message);
424
428
  } catch (err) {
425
429
  logger.error(extractErrorLogData(err), 'Error in DB connection');
426
- process.send({ msgtype: 'error', error: extractErrorMessage(err, 'Error processing message') });
430
+ process.send({
431
+ msgtype: 'error',
432
+ error: extractErrorMessage(err, 'Error processing message'),
433
+ msgid: message?.msgid,
434
+ });
427
435
  }
428
436
  });
429
437
  }
@@ -2,7 +2,7 @@ const stableStringify = require('json-stable-stringify');
2
2
  const { extractBoolSettingsValue, extractIntSettingsValue, getLogger, extractErrorLogData } = require('dbgate-tools');
3
3
  const childProcessChecker = require('../utility/childProcessChecker');
4
4
  const requireEngineDriver = require('../utility/requireEngineDriver');
5
- const connectUtility = require('../utility/connectUtility');
5
+ const { connectUtility } = require('../utility/connectUtility');
6
6
  const { handleProcessCommunication } = require('../utility/processComm');
7
7
  const logger = getLogger('srvconnProcess');
8
8
 
@@ -8,7 +8,7 @@ const { splitQuery } = require('dbgate-query-splitter');
8
8
  const { jsldir } = require('../utility/directories');
9
9
  const requireEngineDriver = require('../utility/requireEngineDriver');
10
10
  const { decryptConnection } = require('../utility/crypting');
11
- const connectUtility = require('../utility/connectUtility');
11
+ const { connectUtility } = require('../utility/connectUtility');
12
12
  const { handleProcessCommunication } = require('../utility/processComm');
13
13
  const { getLogger, extractIntSettingsValue, extractBoolSettingsValue } = require('dbgate-tools');
14
14
 
@@ -245,7 +245,47 @@ async function handleStopProfiler({ jslid }) {
245
245
  currentProfiler = null;
246
246
  }
247
247
 
248
- async function handleExecuteQuery({ sql }) {
248
+ async function handleExecuteControlCommand({ command }) {
249
+ lastActivity = new Date().getTime();
250
+
251
+ await waitConnected();
252
+ const driver = requireEngineDriver(storedConnection);
253
+
254
+ if (command == 'commitTransaction' && !allowExecuteCustomScript(driver)) {
255
+ process.send({
256
+ msgtype: 'info',
257
+ info: {
258
+ message: 'Connection without read-only sessions is read only',
259
+ severity: 'error',
260
+ },
261
+ });
262
+ process.send({ msgtype: 'done', skipFinishedMessage: true });
263
+ return;
264
+ //process.send({ msgtype: 'error', error: e.message });
265
+ }
266
+
267
+ executingScripts++;
268
+ try {
269
+ const dmp = driver.createDumper();
270
+ switch (command) {
271
+ case 'commitTransaction':
272
+ await dmp.commitTransaction();
273
+ break;
274
+ case 'rollbackTransaction':
275
+ await dmp.rollbackTransaction();
276
+ break;
277
+ case 'beginTransaction':
278
+ await dmp.beginTransaction();
279
+ break;
280
+ }
281
+ await driver.query(dbhan, dmp.s, { discardResult: true });
282
+ process.send({ msgtype: 'done', controlCommand: command });
283
+ } finally {
284
+ executingScripts--;
285
+ }
286
+ }
287
+
288
+ async function handleExecuteQuery({ sql, autoCommit }) {
249
289
  lastActivity = new Date().getTime();
250
290
 
251
291
  await waitConnected();
@@ -279,7 +319,7 @@ async function handleExecuteQuery({ sql }) {
279
319
  // handler.stream = stream;
280
320
  // resultIndex = handler.resultIndex;
281
321
  }
282
- process.send({ msgtype: 'done' });
322
+ process.send({ msgtype: 'done', autoCommit });
283
323
  } finally {
284
324
  executingScripts--;
285
325
  }
@@ -323,6 +363,7 @@ function handlePing() {
323
363
  const messageHandlers = {
324
364
  connect: handleConnect,
325
365
  executeQuery: handleExecuteQuery,
366
+ executeControlCommand: handleExecuteControlCommand,
326
367
  executeReader: handleExecuteReader,
327
368
  startProfiler: handleStartProfiler,
328
369
  stopProfiler: handleStopProfiler,
@@ -2,7 +2,7 @@ const stream = require('stream');
2
2
  const path = require('path');
3
3
  const { quoteFullName, fullNameToString, getLogger } = require('dbgate-tools');
4
4
  const requireEngineDriver = require('../utility/requireEngineDriver');
5
- const connectUtility = require('../utility/connectUtility');
5
+ const { connectUtility } = require('../utility/connectUtility');
6
6
  const logger = getLogger('dataDuplicator');
7
7
  const { DataDuplicator } = require('dbgate-datalib');
8
8
  const copyStream = require('./copyStream');
@@ -1,7 +1,7 @@
1
1
  const generateDeploySql = require('./generateDeploySql');
2
2
  const executeQuery = require('./executeQuery');
3
3
  const { ScriptDrivedDeployer } = require('dbgate-datalib');
4
- const connectUtility = require('../utility/connectUtility');
4
+ const { connectUtility } = require('../utility/connectUtility');
5
5
  const requireEngineDriver = require('../utility/requireEngineDriver');
6
6
  const loadModelFolder = require('../utility/loadModelFolder');
7
7
  const crypto = require('crypto');
@@ -1,6 +1,6 @@
1
1
  const executeQuery = require('./executeQuery');
2
2
  const requireEngineDriver = require('../utility/requireEngineDriver');
3
- const connectUtility = require('../utility/connectUtility');
3
+ const { connectUtility } = require('../utility/connectUtility');
4
4
  const { getLogger, extendDatabaseInfo } = require('dbgate-tools');
5
5
 
6
6
  const logger = getLogger('dropAllDbObjects');
@@ -1,5 +1,5 @@
1
1
  const requireEngineDriver = require('../utility/requireEngineDriver');
2
- const connectUtility = require('../utility/connectUtility');
2
+ const { connectUtility } = require('../utility/connectUtility');
3
3
  const { getLogger } = require('dbgate-tools');
4
4
 
5
5
  const logger = getLogger('dumpDb');
@@ -1,6 +1,6 @@
1
1
  const fs = require('fs-extra');
2
2
  const requireEngineDriver = require('../utility/requireEngineDriver');
3
- const connectUtility = require('../utility/connectUtility');
3
+ const { connectUtility } = require('../utility/connectUtility');
4
4
  const { getLogger, getLimitedQuery } = require('dbgate-tools');
5
5
 
6
6
  const logger = getLogger('execQuery');
@@ -13,7 +13,7 @@ const {
13
13
  } = require('dbgate-tools');
14
14
  const importDbModel = require('../utility/importDbModel');
15
15
  const requireEngineDriver = require('../utility/requireEngineDriver');
16
- const connectUtility = require('../utility/connectUtility');
16
+ const { connectUtility } = require('../utility/connectUtility');
17
17
 
18
18
  /**
19
19
  * Generates query for deploying model into database
@@ -1,6 +1,6 @@
1
1
  const fs = require('fs');
2
2
  const requireEngineDriver = require('../utility/requireEngineDriver');
3
- const connectUtility = require('../utility/connectUtility');
3
+ const { connectUtility } = require('../utility/connectUtility');
4
4
  const { splitQueryStream } = require('dbgate-query-splitter/lib/splitQueryStream');
5
5
  const download = require('./download');
6
6
  const stream = require('stream');
@@ -0,0 +1,110 @@
1
+ const path = require('path');
2
+ const fs = require('fs-extra');
3
+ const executeQuery = require('./executeQuery');
4
+ const { connectUtility } = require('../utility/connectUtility');
5
+ const requireEngineDriver = require('../utility/requireEngineDriver');
6
+ const { getAlterDatabaseScript, DatabaseAnalyser, runCommandOnDriver } = require('dbgate-tools');
7
+ const importDbModel = require('../utility/importDbModel');
8
+ const jsonLinesReader = require('./jsonLinesReader');
9
+ const tableWriter = require('./tableWriter');
10
+ const copyStream = require('./copyStream');
11
+
12
+ /**
13
+ * Deploys database model stored in modelFolder (table as yamls) to database
14
+ * @param {object} options
15
+ * @param {connectionType} options.connection - connection object
16
+ * @param {object} options.systemConnection - system connection (result of driver.connect). If not provided, new connection will be created
17
+ * @param {object} options.driver - driver object. If not provided, it will be loaded from connection
18
+ * @param {string} options.folder - folder with model files (YAML files for tables, SQL files for views, procedures, ...)
19
+ * @param {function[]} options.modelTransforms - array of functions for transforming model
20
+ */
21
+ async function importDbFromFolder({ connection, systemConnection, driver, folder, modelTransforms }) {
22
+ if (!driver) driver = requireEngineDriver(connection);
23
+ const dbhan = systemConnection || (await connectUtility(driver, connection, 'read'));
24
+
25
+ try {
26
+ const model = await importDbModel(folder);
27
+
28
+ let modelAdapted = {
29
+ ...model,
30
+ tables: model.tables.map(table => driver.adaptTableInfo(table)),
31
+ };
32
+ for (const transform of modelTransforms || []) {
33
+ modelAdapted = transform(modelAdapted);
34
+ }
35
+
36
+ const modelNoFk = {
37
+ ...modelAdapted,
38
+ tables: modelAdapted.tables.map(table => ({
39
+ ...table,
40
+ foreignKeys: [],
41
+ })),
42
+ };
43
+
44
+ // const plan = createAlterDatabasePlan(
45
+ // DatabaseAnalyser.createEmptyStructure(),
46
+ // driver.dialect.enableAllForeignKeys ? modelAdapted : modelNoFk,
47
+ // {},
48
+ // DatabaseAnalyser.createEmptyStructure(),
49
+ // driver.dialect.enableAllForeignKeys ? modelAdapted : modelNoFk,
50
+ // driver
51
+ // );
52
+ // const dmp1 = driver.createDumper({ useHardSeparator: true });
53
+ // if (driver.dialect.enableAllForeignKeys) {
54
+ // dmp1.enableAllForeignKeys(false);
55
+ // }
56
+ // plan.run(dmp1);
57
+ // if (driver.dialect.enableAllForeignKeys) {
58
+ // dmp1.enableAllForeignKeys(true);
59
+ // }
60
+
61
+ const { sql } = getAlterDatabaseScript(
62
+ DatabaseAnalyser.createEmptyStructure(),
63
+ driver.dialect.enableAllForeignKeys ? modelAdapted : modelNoFk,
64
+ {},
65
+ DatabaseAnalyser.createEmptyStructure(),
66
+ driver.dialect.enableAllForeignKeys ? modelAdapted : modelNoFk,
67
+ driver
68
+ );
69
+ // console.log('CREATING STRUCTURE:', sql);
70
+ await executeQuery({ connection, systemConnection: dbhan, driver, sql, logScriptItems: true });
71
+
72
+ if (driver.dialect.enableAllForeignKeys) {
73
+ await runCommandOnDriver(dbhan, driver, dmp => dmp.enableAllForeignKeys(false));
74
+ }
75
+
76
+ for (const table of modelAdapted.tables) {
77
+ const fileName = path.join(folder, `${table.pureName}.jsonl`);
78
+ if (await fs.exists(fileName)) {
79
+ const src = await jsonLinesReader({ fileName });
80
+ const dst = await tableWriter({
81
+ systemConnection: dbhan,
82
+ pureName: table.pureName,
83
+ driver,
84
+ targetTableStructure: table,
85
+ });
86
+ await copyStream(src, dst);
87
+ }
88
+ }
89
+
90
+ if (driver.dialect.enableAllForeignKeys) {
91
+ await runCommandOnDriver(dbhan, driver, dmp => dmp.enableAllForeignKeys(true));
92
+ } else if (driver.dialect.createForeignKey) {
93
+ const dmp = driver.createDumper();
94
+ for (const table of modelAdapted.tables) {
95
+ for (const fk of table.foreignKeys) {
96
+ dmp.createForeignKey(fk);
97
+ }
98
+ }
99
+
100
+ // create foreign keys
101
+ await executeQuery({ connection, systemConnection: dbhan, driver, sql: dmp.s, logScriptItems: true });
102
+ }
103
+ } finally {
104
+ if (!systemConnection) {
105
+ await driver.close(dbhan);
106
+ }
107
+ }
108
+ }
109
+
110
+ module.exports = importDbFromFolder;
@@ -35,6 +35,7 @@ const sqlTextReplacementTransform = require('./sqlTextReplacementTransform');
35
35
  const autoIndexForeignKeysTransform = require('./autoIndexForeignKeysTransform');
36
36
  const generateDeploySql = require('./generateDeploySql');
37
37
  const dropAllDbObjects = require('./dropAllDbObjects');
38
+ const importDbFromFolder = require('./importDbFromFolder');
38
39
 
39
40
  const dbgateApi = {
40
41
  queryReader,
@@ -73,6 +74,7 @@ const dbgateApi = {
73
74
  autoIndexForeignKeysTransform,
74
75
  generateDeploySql,
75
76
  dropAllDbObjects,
77
+ importDbFromFolder,
76
78
  };
77
79
 
78
80
  requirePlugin.initializeDbgateApi(dbgateApi);
@@ -1,5 +1,5 @@
1
1
  const requireEngineDriver = require('../utility/requireEngineDriver');
2
- const connectUtility = require('../utility/connectUtility');
2
+ const { connectUtility } = require('../utility/connectUtility');
3
3
  const { getLogger } = require('dbgate-tools');
4
4
  const exportDbModel = require('../utility/exportDbModel');
5
5
 
@@ -1,5 +1,5 @@
1
1
  const requireEngineDriver = require('../utility/requireEngineDriver');
2
- const connectUtility = require('../utility/connectUtility');
2
+ const { connectUtility } = require('../utility/connectUtility');
3
3
  const { getLogger } = require('dbgate-tools');
4
4
  const logger = getLogger('queryReader');
5
5
 
@@ -1,6 +1,6 @@
1
1
  const { quoteFullName, fullNameToString, getLogger } = require('dbgate-tools');
2
2
  const requireEngineDriver = require('../utility/requireEngineDriver');
3
- const connectUtility = require('../utility/connectUtility');
3
+ const { connectUtility } = require('../utility/connectUtility');
4
4
  const logger = getLogger('tableReader');
5
5
 
6
6
  /**
@@ -1,6 +1,6 @@
1
1
  const { fullNameToString, getLogger } = require('dbgate-tools');
2
2
  const requireEngineDriver = require('../utility/requireEngineDriver');
3
- const connectUtility = require('../utility/connectUtility');
3
+ const { connectUtility } = require('../utility/connectUtility');
4
4
  const logger = getLogger('tableWriter');
5
5
 
6
6
  /**
@@ -15,6 +15,7 @@ const logger = getLogger('tableWriter');
15
15
  * @param {boolean} options.truncate - truncate table before insert
16
16
  * @param {boolean} options.createIfNotExists - create table if not exists
17
17
  * @param {boolean} options.commitAfterInsert - commit transaction after insert
18
+ * @param {any} options.targetTableStructure - target table structure (don't analyse if given)
18
19
  * @returns {Promise<writerType>} - writer object
19
20
  */
20
21
  async function tableWriter({ connection, schemaName, pureName, driver, systemConnection, ...options }) {
@@ -24,6 +24,18 @@ async function getAwsIamToken(params) {
24
24
  return null;
25
25
  }
26
26
 
27
+ async function callTextToSqlApi(text, structure, dialect) {
28
+ return null;
29
+ }
30
+
31
+ async function callCompleteOnCursorApi(cursorId, query, position, dialect) {
32
+ return null;
33
+ }
34
+
35
+ async function callRefactorSqlQueryApi(query, task, structure, dialect) {
36
+ return null;
37
+ }
38
+
27
39
  module.exports = {
28
40
  isAuthProxySupported,
29
41
  authProxyGetRedirectUrl,
@@ -32,4 +44,7 @@ module.exports = {
32
44
  getAuthProxyUrl,
33
45
  supportsAwsIam,
34
46
  getAwsIamToken,
47
+ callTextToSqlApi,
48
+ callCompleteOnCursorApi,
49
+ callRefactorSqlQueryApi,
35
50
  };
@@ -47,50 +47,32 @@ async function loadConnection(driver, storedConnection, connectionMode) {
47
47
  return storedConnection;
48
48
  }
49
49
 
50
- async function connectUtility(driver, storedConnection, connectionMode, additionalOptions = null) {
51
- const connectionLoaded = await loadConnection(driver, storedConnection, connectionMode);
52
-
53
- const connection = {
54
- database: connectionLoaded.defaultDatabase,
55
- ...decryptConnection(connectionLoaded),
56
- };
57
-
58
- if (!connection.port && driver.defaultPort) connection.port = driver.defaultPort.toString();
59
-
60
- if (connection.useSshTunnel) {
61
- const tunnel = await getSshTunnelProxy(connection);
62
- if (tunnel.state == 'error') {
63
- throw new Error(tunnel.message);
64
- }
65
-
66
- connection.server = tunnel.localHost;
67
- connection.port = tunnel.localPort;
68
- }
69
-
70
- // SSL functionality - copied from https://github.com/beekeeper-studio/beekeeper-studio
50
+ async function extractConnectionSslParams(connection) {
51
+ /** @type {any} */
52
+ let ssl = undefined;
71
53
  if (connection.useSsl) {
72
- connection.ssl = {};
54
+ ssl = {};
73
55
 
74
56
  if (connection.sslCaFile) {
75
- connection.ssl.ca = await fs.readFile(connection.sslCaFile);
76
- connection.ssl.sslCaFile = connection.sslCaFile;
57
+ ssl.ca = await fs.readFile(connection.sslCaFile);
58
+ ssl.sslCaFile = connection.sslCaFile;
77
59
  }
78
60
 
79
61
  if (connection.sslCertFile) {
80
- connection.ssl.cert = await fs.readFile(connection.sslCertFile);
81
- connection.ssl.sslCertFile = connection.sslCertFile;
62
+ ssl.cert = await fs.readFile(connection.sslCertFile);
63
+ ssl.sslCertFile = connection.sslCertFile;
82
64
  }
83
65
 
84
66
  if (connection.sslKeyFile) {
85
- connection.ssl.key = await fs.readFile(connection.sslKeyFile);
86
- connection.ssl.sslKeyFile = connection.sslKeyFile;
67
+ ssl.key = await fs.readFile(connection.sslKeyFile);
68
+ ssl.sslKeyFile = connection.sslKeyFile;
87
69
  }
88
70
 
89
71
  if (connection.sslCertFilePassword) {
90
- connection.ssl.password = connection.sslCertFilePassword;
72
+ ssl.password = connection.sslCertFilePassword;
91
73
  }
92
74
 
93
- if (!connection.ssl.key && !connection.ssl.ca && !connection.ssl.cert) {
75
+ if (!ssl.key && !ssl.ca && !ssl.cert) {
94
76
  // TODO: provide this as an option in settings
95
77
  // or per-connection as 'reject self-signed certs'
96
78
  // How it works:
@@ -98,14 +80,41 @@ async function connectUtility(driver, storedConnection, connectionMode, addition
98
80
  // if true, has to be from a public CA
99
81
  // Heroku certs are self-signed.
100
82
  // if you provide ca/cert/key files, it overrides this
101
- connection.ssl.rejectUnauthorized = false;
83
+ ssl.rejectUnauthorized = false;
102
84
  } else {
103
- connection.ssl.rejectUnauthorized = connection.sslRejectUnauthorized;
85
+ ssl.rejectUnauthorized = connection.sslRejectUnauthorized;
86
+ }
87
+ }
88
+ return ssl;
89
+ }
90
+
91
+ async function connectUtility(driver, storedConnection, connectionMode, additionalOptions = null) {
92
+ const connectionLoaded = await loadConnection(driver, storedConnection, connectionMode);
93
+
94
+ const connection = {
95
+ database: connectionLoaded.defaultDatabase,
96
+ ...decryptConnection(connectionLoaded),
97
+ };
98
+
99
+ if (!connection.port && driver.defaultPort) connection.port = driver.defaultPort.toString();
100
+
101
+ if (connection.useSshTunnel) {
102
+ const tunnel = await getSshTunnelProxy(connection);
103
+ if (tunnel.state == 'error') {
104
+ throw new Error(tunnel.message);
104
105
  }
106
+
107
+ connection.server = tunnel.localHost;
108
+ connection.port = tunnel.localPort;
105
109
  }
106
110
 
111
+ connection.ssl = await extractConnectionSslParams(connection);
112
+
107
113
  const conn = await driver.connect({ ...connection, ...additionalOptions });
108
114
  return conn;
109
115
  }
110
116
 
111
- module.exports = connectUtility;
117
+ module.exports = {
118
+ extractConnectionSslParams,
119
+ connectUtility,
120
+ };
@@ -58,9 +58,11 @@ const jsldir = dirFunc('jsl', true);
58
58
  const rundir = dirFunc('run', true);
59
59
  const uploadsdir = dirFunc('uploads', true);
60
60
  const pluginsdir = dirFunc('plugins');
61
- const archivedir = dirFunc('archive', false, ['default']);
61
+ const archivedir = processArgs.runE2eTests
62
+ ? dirFunc('archive-e2etests', false, ['default'])
63
+ : dirFunc('archive', false, ['default']);
62
64
  const appdir = dirFunc('apps');
63
- const filesdir = dirFunc('files');
65
+ const filesdir = processArgs.runE2eTests ? dirFunc('files-e2etests') : dirFunc('files');
64
66
  const logsdir = dirFunc('logs', 3600 * 24 * 7);
65
67
 
66
68
  function packagedPluginsDir() {
@@ -80,6 +82,9 @@ function packagedPluginsDir() {
80
82
  if (platformInfo.isAwsUbuntuLayout) {
81
83
  return '/home/ubuntu/build/plugins';
82
84
  }
85
+ if (platformInfo.isAzureUbuntuLayout) {
86
+ return '/home/azureuser/build/plugins';
87
+ }
83
88
  if (platformInfo.isNpmDist) {
84
89
  // node_modules
85
90
  return global['PLUGINS_DIR'];
@@ -73,6 +73,7 @@ async function getPublicHardwareFingerprint() {
73
73
  region: fingerprint.region,
74
74
  isDocker: platformInfo.isDocker,
75
75
  isAwsUbuntuLayout: platformInfo.isAwsUbuntuLayout,
76
+ isAzureUbuntuLayout: platformInfo.isAzureUbuntuLayout,
76
77
  isElectron: platformInfo.isElectron,
77
78
  },
78
79
  };
@@ -15,6 +15,7 @@ const isNpmDist = !!global['IS_NPM_DIST'];
15
15
  const isDbModel = !!global['IS_DB_MODEL'];
16
16
  const isForkedApi = processArgs.isForkedApi;
17
17
  const isAwsUbuntuLayout = fs.existsSync('/home/ubuntu/build/public');
18
+ const isAzureUbuntuLayout = fs.existsSync('/home/azureuser/build/public');
18
19
 
19
20
  // function moduleAvailable(name) {
20
21
  // try {
@@ -57,6 +58,7 @@ const platformInfo = {
57
58
  allowConnectionFromEnvVariables: !!isDbModel,
58
59
  defaultKeyfile: path.join(os.homedir(), '.ssh/id_rsa'),
59
60
  isAwsUbuntuLayout,
61
+ isAzureUbuntuLayout,
60
62
  };
61
63
 
62
64
  module.exports = platformInfo;
@@ -28,9 +28,9 @@ function getPassArgs() {
28
28
  if (listenApiChild) {
29
29
  res.push('listen-api-child');
30
30
  }
31
- // if (runE2eTests) {
32
- // res.push('--run-e2e-tests');
33
- // }
31
+ if (runE2eTests) {
32
+ res.push('--run-e2e-tests');
33
+ }
34
34
  return res;
35
35
  }
36
36
 
@@ -86,7 +86,7 @@ module.exports = function useController(app, electron, route, controller) {
86
86
  detail: err.detail,
87
87
  });
88
88
  } else {
89
- res.status(500).json({ apiErrorMessage: err.message });
89
+ res.status(500).json({ apiErrorMessage: (_.isString(err) ? err : err.message) ?? 'Unknown error' });
90
90
  }
91
91
  }
92
92
  });