diff --git a/packages/api/package.json b/packages/api/package.json index 8d8ee1eff..4be96950b 100644 --- a/packages/api/package.json +++ b/packages/api/package.json @@ -36,6 +36,7 @@ "fs-reverse": "^0.0.3", "get-port": "^5.1.1", "http": "^0.0.0", + "js-yaml": "^4.1.0", "json-stable-stringify": "^1.0.1", "line-reader": "^0.4.0", "lodash": "^4.17.21", diff --git a/packages/api/src/controllers/databaseConnections.js b/packages/api/src/controllers/databaseConnections.js index 15aa3e3b4..20080faf7 100644 --- a/packages/api/src/controllers/databaseConnections.js +++ b/packages/api/src/controllers/databaseConnections.js @@ -6,6 +6,10 @@ const { fork } = require('child_process'); const { DatabaseAnalyser } = require('dbgate-tools'); const { handleProcessCommunication } = require('../utility/processComm'); const config = require('./config'); +const fs = require('fs-extra'); +const exportDbModel = require('../utility/exportDbModel'); +const { archivedir } = require('../utility/directories'); +const path = require('path'); module.exports = { /** @type {import('dbgate-types').OpenedDatabaseConnection[]} */ @@ -242,6 +246,10 @@ module.exports = { exportModel_meta: 'post', async exportModel({ conid, database }) { const archiveFolder = await archive.getNewArchiveFolder({ database }); + await fs.mkdir(path.join(archivedir(), archiveFolder)); + const model = await this.structure({ conid, database }); + await exportDbModel(model, path.join(archivedir(), archiveFolder)); + socket.emitChanged(`archive-folders-changed`); return { archiveFolder }; }, diff --git a/packages/api/src/utility/exportDbModel.js b/packages/api/src/utility/exportDbModel.js new file mode 100644 index 000000000..526eec785 --- /dev/null +++ b/packages/api/src/utility/exportDbModel.js @@ -0,0 +1,31 @@ +const fs = require('fs-extra'); +const path = require('path'); +const yaml = require('js-yaml'); +const { tableInfoToYaml } = require('dbgate-tools'); + +async function exportDbModel(dbModel, outputDir) { + const { tables, views, procedures, functions, triggers, matviews } = dbModel; + + if (!fs.existsSync(outputDir)) { + await fs.mkdir(outputDir); + } + + for (const table of tables || []) { + const content = yaml.dump(tableInfoToYaml(table)); + await fs.writeFile(path.join(outputDir, `${table.pureName}.table.yaml`), content); + } + + async function writeList(list, ext) { + for (const obj of list || []) { + await fs.writeFile(path.join(outputDir, `${obj.pureName}.${ext}.sql`), obj.createSql); + } + } + + await writeList(views, 'view'); + await writeList(procedures, 'proc'); + await writeList(functions, 'func'); + await writeList(triggers, 'trigger'); + await writeList(matviews, 'matview'); +} + +module.exports = exportDbModel; diff --git a/packages/tools/src/index.ts b/packages/tools/src/index.ts index 5485b923a..5845468a9 100644 --- a/packages/tools/src/index.ts +++ b/packages/tools/src/index.ts @@ -13,3 +13,4 @@ export * from './settingsExtractors'; export * from './filterName'; export * from './diffTools'; export * from './schemaEditorTools'; +export * from './yamlModelConv'; diff --git a/packages/tools/src/yamlModelConv.ts b/packages/tools/src/yamlModelConv.ts new file mode 100644 index 000000000..76c6c1f35 --- /dev/null +++ b/packages/tools/src/yamlModelConv.ts @@ -0,0 +1,70 @@ +import { ColumnInfo, TableInfo } from 'dbgate-types'; +import _ from 'lodash'; +import _cloneDeep from 'lodash/cloneDeep'; + +export interface ColumnInfoYaml { + name: string; + type: string; + notNull?: boolean; + autoIncrement?: boolean; + references?: string; + primaryKey?: boolean; +} + +export interface TableInfoYaml { + name: string; + // schema?: string; + columns: ColumnInfoYaml[]; + primaryKey?: string[]; +} + +export interface ForeignKeyInfoYaml { + deleteAction?: string; +} + +function foreignKeyInfoToYaml() { + +} + +function columnInfoToYaml(column: ColumnInfo, table: TableInfo): ColumnInfoYaml { + const res: ColumnInfoYaml = { + name: column.columnName, + type: column.dataType, + }; + if (column.autoIncrement) res.autoIncrement = true; + if (column.notNull) res.notNull = true; + + const fk = + table.foreignKeys && + table.foreignKeys.find(fk => fk.columns.length == 1 && fk.columns[0].columnName == column.columnName); + if ( + fk && + // fk.refSchemaName == table.schemaName && + (!fk.deleteAction || fk.deleteAction == 'NO ACTION') && + (!fk.updateAction || fk.updateAction == 'NO ACTION') + ) { + res.references = fk.refTableName; + fk['_dumped'] = true; + } + + // if (table.primaryKey && table.primaryKey.columns.length == 1) { + // table.primaryKey['_dumped'] = true; + // res.primaryKey = true; + // } + + return res; +} + +export function tableInfoToYaml(table: TableInfo): TableInfoYaml { + const tableCopy = _cloneDeep(table); + const res: TableInfoYaml = { + name: tableCopy.pureName, + // schema: tableCopy.schemaName, + columns: tableCopy.columns.map(c => columnInfoToYaml(c, tableCopy)), + }; + if (tableCopy.primaryKey && !tableCopy.primaryKey['_dumped']) { + res.primaryKey = tableCopy.primaryKey.columns.map(x => x.columnName); + } + const foreignKeys = (tableCopy.foreignKeys || []).filter(x => !x['_dumped']).map(foreignKeyInfoToYaml); + return res; +} diff --git a/yarn.lock b/yarn.lock index 27759e55b..cfe1cad17 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1717,6 +1717,11 @@ argparse@^1.0.7: dependencies: sprintf-js "~1.0.2" +argparse@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/argparse/-/argparse-2.0.1.tgz#246f50f3ca78a3240f6c997e8a9bd1eac49e4b38" + integrity sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q== + arr-diff@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/arr-diff/-/arr-diff-4.0.0.tgz#d6461074febfec71e7e15235761a329a5dc7c520" @@ -6417,6 +6422,13 @@ js-yaml@^3.13.1: argparse "^1.0.7" esprima "^4.0.0" +js-yaml@^4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-4.1.0.tgz#c1fb65f8f5017901cdd2c951864ba18458a10602" + integrity sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA== + dependencies: + argparse "^2.0.1" + jsbi@^3.1.3: version "3.1.4" resolved "https://registry.yarnpkg.com/jsbi/-/jsbi-3.1.4.tgz#9654dd02207a66a4911b4e4bb74265bc2cbc9dd0"