dbmodel commandline tool

This commit is contained in:
Jan Prochazka
2023-01-28 20:14:44 +01:00
parent 258338cd2e
commit bc1c827225
9 changed files with 162 additions and 109 deletions

View File

@@ -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;

View File

@@ -24,6 +24,7 @@ const initializeApiEnvironment = require('./initializeApiEnvironment');
const dumpDatabase = require('./dumpDatabase'); const dumpDatabase = require('./dumpDatabase');
const importDatabase = require('./importDatabase'); const importDatabase = require('./importDatabase');
const loadDatabase = require('./loadDatabase'); const loadDatabase = require('./loadDatabase');
const generateModelSql = require('./generateModelSql');
const dbgateApi = { const dbgateApi = {
queryReader, queryReader,
@@ -51,6 +52,7 @@ const dbgateApi = {
dumpDatabase, dumpDatabase,
importDatabase, importDatabase,
loadDatabase, loadDatabase,
generateModelSql,
}; };
requirePlugin.initializeDbgateApi(dbgateApi); requirePlugin.initializeDbgateApi(dbgateApi);

View File

@@ -43,41 +43,15 @@ Model is stored as a collection of files:
```sh ```sh
# load from existing database # 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 # 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 # 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 ## Table yaml file documentation
```yaml ```yaml

View File

@@ -10,8 +10,21 @@ global.PLUGINS_DIR = process.env.DEVMODE
global.IS_NPM_DIST = true; global.IS_NPM_DIST = true;
const program = require('commander'); const program = require('commander');
const dbmodel = require('../lib');
const dbgateApi = require('dbgate-api'); 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 program
.option('-s, --server <server>', 'server host') .option('-s, --server <server>', 'server host')
@@ -26,14 +39,15 @@ program
.requiredOption('-e, --engine <engine>', 'engine name, eg. mysql@dbgate-plugin-mysql'); .requiredOption('-e, --engine <engine>', 'engine name, eg. mysql@dbgate-plugin-mysql');
program program
.command('deploy <projectDir>') .command('deploy <modelFolder>')
.description('Deploys model to database') .description('Deploys model to database')
.action(projectDir => { .action(modelFolder => {
const { engine, server, user, password, database } = program; const { engine, server, user, password, database } = program.opts();
const hooks = []; // const hooks = [];
if (program.autoIndexForeignKeys) hooks.push(dbmodel.hooks.autoIndexForeignKeys); // if (program.autoIndexForeignKeys) hooks.push(dbmodel.hooks.autoIndexForeignKeys);
dbmodel.runAndExit(
dbmodel.deploy({ runAndExit(
dbgateApi.deployDb({
connection: { connection: {
engine, engine,
server, server,
@@ -41,12 +55,25 @@ program
password, password,
database, database,
}, },
hooks, modelFolder,
projectDir,
}) })
); );
}); });
// runAndExit(
// dbmodel.deploy({
// connection: {
// engine,
// server,
// user,
// password,
// database,
// },
// hooks,
// projectDir,
// })
// );
program program
.command('load <outputDir>') .command('load <outputDir>')
.description('Loads model from database') .description('Loads model from database')
@@ -57,7 +84,7 @@ program
// : null; // : null;
// const hooks = []; // const hooks = [];
dbmodel.runAndExit( runAndExit(
dbgateApi.loadDatabase({ dbgateApi.loadDatabase({
connection: { connection: {
engine, engine,
@@ -72,17 +99,18 @@ program
}); });
program program
.command('build <projectDir> <outputFile>') .command('build <modelFolder> <outputFile>')
.description('Builds single SQL script from project') .description('Builds single SQL script from project')
.action((projectDir, outputFile) => { .action((modelFolder, outputFile) => {
const { client } = program; const { engine } = program.opts();
const hooks = []; // const hooks = [];
dbmodel.runAndExit( runAndExit(
dbmodel.build({ dbgateApi.generateModelSql({
client, // client,
hooks, // hooks,
projectDir, modelFolder,
outputFile, outputFile,
engine,
}) })
); );
}); });

View File

@@ -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,
};

View File

@@ -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;

View File

@@ -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;

View File

@@ -19,7 +19,9 @@
], ],
"scripts": { "scripts": {
"dbmodel": "node ./bin/dbmodel.js", "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": { "dependencies": {
"commander": "^10.0.0", "commander": "^10.0.0",

76
packages/dbmodel/testdata/db.sql vendored Normal file
View File

@@ -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;