diff --git a/packages/api/src/shell/deployDb.js b/packages/api/src/shell/deployDb.js new file mode 100644 index 000000000..a2fe4cf2b --- /dev/null +++ b/packages/api/src/shell/deployDb.js @@ -0,0 +1,8 @@ +const EnsureStreamHeaderStream = require('../utility/EnsureStreamHeaderStream'); +const importDbModel = require('../utility/importDbModel'); + +async function deployDb(connection, modelFolder, options) { + const dbModel = await importDbModel(modelFolder); +} + +module.exports = deployDb; diff --git a/packages/api/src/utility/importDbModel.js b/packages/api/src/utility/importDbModel.js new file mode 100644 index 000000000..760dfd145 --- /dev/null +++ b/packages/api/src/utility/importDbModel.js @@ -0,0 +1,65 @@ +const fs = require('fs-extra'); +const path = require('path'); +const yaml = require('js-yaml'); +const { tableInfoFromYaml, DatabaseAnalyser } = require('dbgate-tools'); + +async function importDbModel(inputDir) { + const tablesYaml = []; + + const model = DatabaseAnalyser.createEmptyStructure(); + + for (const file of await fs.readdir(inputDir)) { + if (file.endsWith('.table.yaml') || file.endsWith('.sql')) { + const content = await fs.readFile(path.join(inputDir, file), { encoding: 'utf-8' }); + + if (file.endsWith('.table.yaml')) { + const json = yaml.load(content); + tablesYaml.push(json); + } + + + if (file.endsWith('.view.sql')) { + model.views.push({ + pureName: file.slice(0, -'.view.sql'.length), + createSql: content, + columns: [], + }); + } + + if (file.endsWith('.matview.sql')) { + model.matviews.push({ + pureName: file.slice(0, -'.matview.sql'.length), + createSql: content, + columns: [], + }); + } + + if (file.endsWith('.proc.sql')) { + model.procedures.push({ + pureName: file.slice(0, -'.proc.sql'.length), + createSql: content, + }); + } + + if (file.endsWith('.func.sql')) { + model.functions.push({ + pureName: file.slice(0, -'.func.sql'.length), + createSql: content, + }); + } + + if (file.endsWith('.trigger.sql')) { + model.triggers.push({ + pureName: file.slice(0, -'.trigger.sql'.length), + createSql: content, + }); + } + } + } + + model.tables = tablesYaml.map(table => tableInfoFromYaml(table, tablesYaml)); + + return model; +} + +module.exports = importDbModel; diff --git a/packages/tools/src/yamlModelConv.ts b/packages/tools/src/yamlModelConv.ts index 76c6c1f35..9f51c8fac 100644 --- a/packages/tools/src/yamlModelConv.ts +++ b/packages/tools/src/yamlModelConv.ts @@ -1,4 +1,4 @@ -import { ColumnInfo, TableInfo } from 'dbgate-types'; +import { ColumnInfo, TableInfo, ForeignKeyInfo } from 'dbgate-types'; import _ from 'lodash'; import _cloneDeep from 'lodash/cloneDeep'; @@ -19,12 +19,10 @@ export interface TableInfoYaml { } export interface ForeignKeyInfoYaml { - deleteAction?: string; + deleteAction?: string; } -function foreignKeyInfoToYaml() { - -} +// function foreignKeyInfoToYaml() {} function columnInfoToYaml(column: ColumnInfo, table: TableInfo): ColumnInfoYaml { const res: ColumnInfoYaml = { @@ -55,6 +53,17 @@ function columnInfoToYaml(column: ColumnInfo, table: TableInfo): ColumnInfoYaml return res; } +function columnInfoFromYaml(column: ColumnInfoYaml, table: TableInfoYaml): ColumnInfo { + const res: ColumnInfo = { + pureName: table.name, + columnName: column.name, + dataType: column.type, + autoIncrement: column.autoIncrement, + notNull: column.notNull, + }; + return res; +} + export function tableInfoToYaml(table: TableInfo): TableInfoYaml { const tableCopy = _cloneDeep(table); const res: TableInfoYaml = { @@ -65,6 +74,40 @@ export function tableInfoToYaml(table: TableInfo): TableInfoYaml { 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); + // const foreignKeys = (tableCopy.foreignKeys || []).filter(x => !x['_dumped']).map(foreignKeyInfoToYaml); + return res; +} + +function convertForeignKeyFromYaml(col: ColumnInfoYaml, table: TableInfoYaml, allTables: TableInfoYaml[]): ForeignKeyInfo { + const refTable = allTables.find(x => x.name == col.references); + if (!refTable || !refTable.primaryKey) return null; + return { + constraintType: 'foreignKey', + pureName: table.name, + refTableName: col.references, + columns: [ + { + columnName: col.name, + refColumnName: refTable.primaryKey[0], + }, + ], + }; +} + +export function tableInfoFromYaml(table: TableInfoYaml, allTables: TableInfoYaml[]): TableInfo { + const res: TableInfo = { + pureName: table.name, + columns: table.columns.map(c => columnInfoFromYaml(c, table)), + foreignKeys: _.compact( + table.columns.filter(x => x.references).map(col => convertForeignKeyFromYaml(col, table, allTables)) + ), + }; + if (table.primaryKey) { + res.primaryKey = { + pureName: table.name, + constraintType: 'primaryKey', + columns: table.primaryKey.map(columnName => ({ columnName })), + }; + } return res; } diff --git a/packages/types/dbinfo.d.ts b/packages/types/dbinfo.d.ts index f4fdaf669..3f5f655e9 100644 --- a/packages/types/dbinfo.d.ts +++ b/packages/types/dbinfo.d.ts @@ -23,10 +23,10 @@ export interface ColumnsConstraintInfo extends ConstraintInfo { export interface PrimaryKeyInfo extends ColumnsConstraintInfo {} export interface ForeignKeyInfo extends ColumnsConstraintInfo { - refSchemaName: string; + refSchemaName?: string; refTableName: string; - updateAction: string; - deleteAction: string; + updateAction?: string; + deleteAction?: string; } export interface IndexInfo extends ColumnsConstraintInfo { @@ -46,14 +46,14 @@ export interface ColumnInfo extends NamedObjectInfo { notNull: boolean; autoIncrement: boolean; dataType: string; - precision: number; - scale: number; - length: number; - computedExpression: string; - isPersisted: boolean; - isSparse: boolean; - defaultValue: string; - defaultConstraint: string; + precision?: number; + scale?: number; + length?: number; + computedExpression?: string; + isPersisted?: boolean; + isSparse?: boolean; + defaultValue?: string; + defaultConstraint?: string; } export interface DatabaseObjectInfo extends NamedObjectInfo { diff --git a/packages/web/src/appobj/ArchiveFolderAppObject.svelte b/packages/web/src/appobj/ArchiveFolderAppObject.svelte index 06e9bf8f1..693f7643c 100644 --- a/packages/web/src/appobj/ArchiveFolderAppObject.svelte +++ b/packages/web/src/appobj/ArchiveFolderAppObject.svelte @@ -9,6 +9,7 @@ import { currentArchive } from '../stores'; import axiosInstance from '../utility/axiosInstance'; + import openNewTab from '../utility/openNewTab'; import AppObjectCore from './AppObjectCore.svelte'; export let data; @@ -17,8 +18,22 @@ axiosInstance.post('archive/delete-folder', { folder: data.name }); }; + const handleGenerateDeployScript = () => { + openNewTab( + { + title: 'Shell #', + icon: 'img shell', + tabComponent: 'ShellTab', + }, + { editor: `await dbgateApi.deployDb()` } + ); + }; + function createMenu() { - return [data.name != 'default' && { text: 'Delete', onClick: handleDelete }]; + return [ + data.name != 'default' && { text: 'Delete', onClick: handleDelete }, + data.name != 'default' && { text: 'Generate deploy script', onClick: handleGenerateDeployScript }, + ]; }