diff --git a/packages/api/src/shell/generateModelSql.js b/packages/api/src/shell/generateModelSql.js new file mode 100644 index 000000000..7b969b155 --- /dev/null +++ b/packages/api/src/shell/generateModelSql.js @@ -0,0 +1,30 @@ +const requireEngineDriver = require('../utility/requireEngineDriver'); +const { + extendDatabaseInfo, + databaseInfoFromYamlModel, + getAlterDatabaseScript, + DatabaseAnalyser, +} = require('dbgate-tools'); +const importDbModel = require('../utility/importDbModel'); +const fs = require('fs'); + +async function generateModelSql({ engine, driver, modelFolder, loadedDbModel, outputFile }) { + if (!driver) driver = requireEngineDriver(engine); + + const dbInfo = extendDatabaseInfo( + loadedDbModel ? databaseInfoFromYamlModel(loadedDbModel) : await importDbModel(modelFolder) + ); + + const { sql } = getAlterDatabaseScript( + DatabaseAnalyser.createEmptyStructure(), + dbInfo, + {}, + DatabaseAnalyser.createEmptyStructure(), + dbInfo, + driver + ); + + fs.writeFileSync(outputFile, sql); +} + +module.exports = generateModelSql; diff --git a/packages/api/src/shell/index.js b/packages/api/src/shell/index.js index 3fe5847b2..420a4f65f 100644 --- a/packages/api/src/shell/index.js +++ b/packages/api/src/shell/index.js @@ -24,6 +24,7 @@ const initializeApiEnvironment = require('./initializeApiEnvironment'); const dumpDatabase = require('./dumpDatabase'); const importDatabase = require('./importDatabase'); const loadDatabase = require('./loadDatabase'); +const generateModelSql = require('./generateModelSql'); const dbgateApi = { queryReader, @@ -51,6 +52,7 @@ const dbgateApi = { dumpDatabase, importDatabase, loadDatabase, + generateModelSql, }; requirePlugin.initializeDbgateApi(dbgateApi); diff --git a/packages/dbmodel/README.md b/packages/dbmodel/README.md index e0e75481e..4ac2e5362 100644 --- a/packages/dbmodel/README.md +++ b/packages/dbmodel/README.md @@ -43,41 +43,15 @@ Model is stored as a collection of files: ```sh # load from existing database -dbmodel load -s localhost -u USERNAME -p PASSWORD -d DATABASE -c mssql OUTPUT_FOLDER +dbmodel load -s localhost -u USERNAME -p PASSWORD -d DATABASE -e mssql@dbgate-plugin-mssql OUTPUT_FOLDER # deploy project to database -dbmodel deploy -s localhost -u USERNAME -p PASSWORD -d DATABASE -c mssql PROJECT_FOLDER +dbmodel deploy -s localhost -u USERNAME -p PASSWORD -d DATABASE -e mssql@dbgate-plugin-mssql PROJECT_FOLDER # build SQL script from project -dbmodel build -c mssql PROJECT_FOLDER OUTPUT_FILE.sql +dbmodel build -e mssql@dbgate-plugin-mssql PROJECT_FOLDER OUTPUT_FILE.sql ``` -## JavaScript interface - -```javascript -const dbmodel = require('dbmodel'); - -await dbmodel.deploy({ - client: 'mssql', - connection: { - server: '127.0.0.1', - user: process.env.DB_USER, - password: process.env.DB_PASSWORD, - database: 'Chinook_Model', - }, - hooks: [dbmodel.hooks.autoIndexForeignKeys], // this hook adds indexes to all foreign keys - projectDir: 'model', -}) -``` - -list of dbmodel exported functions: -* build - builds SQL script -* deploy - deploys model to database -* dump - dumps loaded model into directory -* load - loads model from database -* read - reads model from directory -* connect - creates database connection defined in options - ## Table yaml file documentation ```yaml diff --git a/packages/dbmodel/bin/dbmodel.js b/packages/dbmodel/bin/dbmodel.js index b1db97fdc..8c666380c 100755 --- a/packages/dbmodel/bin/dbmodel.js +++ b/packages/dbmodel/bin/dbmodel.js @@ -10,8 +10,21 @@ global.PLUGINS_DIR = process.env.DEVMODE global.IS_NPM_DIST = true; const program = require('commander'); -const dbmodel = require('../lib'); const dbgateApi = require('dbgate-api'); +const { createLogger } = require('pinomin'); + +const logger = createLogger('dbmodel'); + +async function runAndExit(promise) { + try { + await promise; + logger.info('Success'); + process.exit(); + } catch (err) { + logger.error({ err }, 'Processing failed'); + process.exit(1); + } +} program .option('-s, --server ', 'server host') @@ -26,14 +39,15 @@ program .requiredOption('-e, --engine ', 'engine name, eg. mysql@dbgate-plugin-mysql'); program - .command('deploy ') + .command('deploy ') .description('Deploys model to database') - .action(projectDir => { - const { engine, server, user, password, database } = program; - const hooks = []; - if (program.autoIndexForeignKeys) hooks.push(dbmodel.hooks.autoIndexForeignKeys); - dbmodel.runAndExit( - dbmodel.deploy({ + .action(modelFolder => { + const { engine, server, user, password, database } = program.opts(); + // const hooks = []; + // if (program.autoIndexForeignKeys) hooks.push(dbmodel.hooks.autoIndexForeignKeys); + + runAndExit( + dbgateApi.deployDb({ connection: { engine, server, @@ -41,12 +55,25 @@ program password, database, }, - hooks, - projectDir, + modelFolder, }) ); }); +// runAndExit( +// dbmodel.deploy({ +// connection: { +// engine, +// server, +// user, +// password, +// database, +// }, +// hooks, +// projectDir, +// }) +// ); + program .command('load ') .description('Loads model from database') @@ -57,7 +84,7 @@ program // : null; // const hooks = []; - dbmodel.runAndExit( + runAndExit( dbgateApi.loadDatabase({ connection: { engine, @@ -72,17 +99,18 @@ program }); program - .command('build ') + .command('build ') .description('Builds single SQL script from project') - .action((projectDir, outputFile) => { - const { client } = program; - const hooks = []; - dbmodel.runAndExit( - dbmodel.build({ - client, - hooks, - projectDir, + .action((modelFolder, outputFile) => { + const { engine } = program.opts(); + // const hooks = []; + runAndExit( + dbgateApi.generateModelSql({ + // client, + // hooks, + modelFolder, outputFile, + engine, }) ); }); diff --git a/packages/dbmodel/lib/index.js b/packages/dbmodel/lib/index.js deleted file mode 100644 index 0edd0c4b0..000000000 --- a/packages/dbmodel/lib/index.js +++ /dev/null @@ -1,20 +0,0 @@ -const load = require('./load'); -// const connect = require('./connect'); -// const dump = require('./dump'); -// const deploy = require('./deploy'); -// const build = require('./build'); -// const efmodel = require('./efmodel'); - -// const hooks = require('./hooks'); -const runAndExit = require('./runAndExit'); - -module.exports = { - load, - // connect, - // dump, - // deploy, - // hooks, - // build, - // efmodel, - runAndExit, -}; diff --git a/packages/dbmodel/lib/load.js b/packages/dbmodel/lib/load.js deleted file mode 100644 index 646a4d728..000000000 --- a/packages/dbmodel/lib/load.js +++ /dev/null @@ -1,23 +0,0 @@ -const _ = require('lodash'); -const dbgateApi = require('dbgate-api'); - -async function load({ connection, outputDir }) { - await dbgateApi.loadDatabase(connection, outputDir); - - // await connect(options); - // const { client } = options; - // const loadFunc = require(`./clients/${client}/load`); - // options.databaseStructure = await loadFunc(options); - // if (options.loadDataCondition) { - // const { tables } = options.databaseStructure; - // for (const tableName of _.keys(tables)) { - // const table = tables[tableName]; - // if (!options.loadDataCondition(table)) continue; - // const data = await query(options, `SELECT * FROM [${tableName}]`); - // table.data = data; - // } - // } - // return options; -} - -module.exports = load; diff --git a/packages/dbmodel/lib/runAndExit.js b/packages/dbmodel/lib/runAndExit.js deleted file mode 100644 index eb2eafef9..000000000 --- a/packages/dbmodel/lib/runAndExit.js +++ /dev/null @@ -1,16 +0,0 @@ -const { createLogger } = require('pinomin'); - -const logger = createLogger('runAndExit'); - -async function runAndExit(promise) { - try { - await promise; - logger.info('Success'); - process.exit(); - } catch (err) { - logger.error({ err }, 'Processing failed'); - process.exit(1); - } -} - -module.exports = runAndExit; diff --git a/packages/dbmodel/package.json b/packages/dbmodel/package.json index c5ff5b9b1..b0d1ea3f9 100644 --- a/packages/dbmodel/package.json +++ b/packages/dbmodel/package.json @@ -19,7 +19,9 @@ ], "scripts": { "dbmodel": "node ./bin/dbmodel.js", - "dbmodel:load": "cross-env DEVMODE=1 node ./bin/dbmodel.js load testdata/db -e postgres@dbgate-plugin-postgres -s localhost -u postgres -p Pwd2020Db -d zradlo" + "dbmodel:load": "cross-env DEVMODE=1 node ./bin/dbmodel.js load testdata/db -e postgres@dbgate-plugin-postgres -s localhost -u postgres -p Pwd2020Db -d zradlo", + "dbmodel:deploy": "cross-env DEVMODE=1 node ./bin/dbmodel.js deploy testdata/db -e postgres@dbgate-plugin-postgres -s localhost -u postgres -p Pwd2020Db -d deployed", + "dbmodel:build": "cross-env DEVMODE=1 node ./bin/dbmodel.js build testdata/db testdata/db.sql -e postgres@dbgate-plugin-postgres " }, "dependencies": { "commander": "^10.0.0", diff --git a/packages/dbmodel/testdata/db.sql b/packages/dbmodel/testdata/db.sql new file mode 100644 index 000000000..26981cb5b --- /dev/null +++ b/packages/dbmodel/testdata/db.sql @@ -0,0 +1,76 @@ +BEGIN TRANSACTION; +CREATE TABLE "language" ( + "language_id" CHARACTER(31) NOT NULL, + "name" VARCHAR(255) NULL, + PRIMARY KEY ("language_id") +); +CREATE TABLE "preparation_step" ( + "preparation_step_id" SERIAL, + "recipe_id" INTEGER NULL, + "description" VARCHAR NULL, + "duration" INTERVAL NULL, + "step_order" SMALLINT NULL, + "language_id" CHARACTER(31) NULL, + PRIMARY KEY ("preparation_step_id") +); +CREATE TABLE "recipe" ( + "recipe_id" SERIAL, + "name" VARCHAR(1000) NOT NULL, + "is_public" BOOLEAN NULL, + "user_id" INTEGER NULL, + "source_url" VARCHAR(1000) NULL, + "cook_duration" INTERVAL NULL, + "servings" SMALLINT NULL, + "language_id" CHARACTER(31) NULL, + "deleted_date" TIMESTAMP NULL, + PRIMARY KEY ("recipe_id") +); +CREATE TABLE "recipe_has_ingredient" ( + "recipe_id" INTEGER NOT NULL, + "recipe_ingredient_id" INTEGER NOT NULL, + "unit_id" INTEGER NULL, + "unit" VARCHAR(100) NULL, + "name" VARCHAR(1000) NULL, + "amount" REAL NULL, + PRIMARY KEY ("recipe_id", "recipe_ingredient_id") +); +CREATE TABLE "recipe_photo" ( + "recipe_photo_id" SERIAL, + "path" VARCHAR NULL, + "name" VARCHAR(255) NULL, + "recipe_id" INTEGER NULL, + "user_id" INTEGER NULL, + "created_on" TIMESTAMP NULL, + PRIMARY KEY ("recipe_photo_id") +); +CREATE TABLE "shop" ( + "shop_id" SERIAL, + "name" VARCHAR(255) NULL, + "url" VARCHAR NULL, + "logo_path" VARCHAR NULL, + PRIMARY KEY ("shop_id") +); +CREATE TABLE "unit" ( + "unit_id" SERIAL, + "name" VARCHAR(255) NOT NULL, + "user_id" INTEGER NULL, + PRIMARY KEY ("unit_id") +); +CREATE TABLE "user" ( + "user_id" SERIAL, + "name" VARCHAR(255) NULL, + "email" VARCHAR(255) NULL, + "password_hash" VARCHAR NULL, + "last_logged_on" TIMESTAMP NULL, + "user_settings_id" INTEGER NULL, + PRIMARY KEY ("user_id") +); +ALTER TABLE "preparation_step" ADD FOREIGN KEY ("recipe_id") REFERENCES "recipe" ("recipe_id"); +ALTER TABLE "preparation_step" ADD FOREIGN KEY ("language_id") REFERENCES "language" ("language_id"); +ALTER TABLE "recipe" ADD FOREIGN KEY ("user_id") REFERENCES "user" ("user_id"); +ALTER TABLE "recipe" ADD FOREIGN KEY ("language_id") REFERENCES "language" ("language_id"); +ALTER TABLE "recipe_has_ingredient" ADD FOREIGN KEY ("recipe_id") REFERENCES "recipe" ("recipe_id"); +ALTER TABLE "recipe_has_ingredient" ADD FOREIGN KEY ("unit_id") REFERENCES "unit" ("unit_id"); +ALTER TABLE "recipe_photo" ADD FOREIGN KEY ("recipe_id") REFERENCES "recipe" ("recipe_id"); +ALTER TABLE "recipe_photo" ADD FOREIGN KEY ("user_id") REFERENCES "user" ("user_id"); +COMMIT;