2020-02-20 15:22:33 +01:00
|
|
|
/* eslint-disable no-magic-numbers */
|
|
|
|
|
2020-02-20 11:09:01 +01:00
|
|
|
'use strict';
|
|
|
|
|
|
|
|
const fs = require ('fs-extra');
|
|
|
|
|
|
|
|
/**
|
2020-02-20 15:22:33 +01:00
|
|
|
* convert short type notation to knex notation
|
|
|
|
*
|
|
|
|
* @param {string} short short type
|
|
|
|
* @returns {string} type
|
|
|
|
*/
|
|
|
|
function get_type (short) {
|
|
|
|
switch (short) {
|
|
|
|
case '\'\'':
|
|
|
|
return 'string';
|
|
|
|
case '#':
|
|
|
|
return 'integer';
|
|
|
|
case '#.#':
|
|
|
|
return 'double';
|
|
|
|
case '✓':
|
|
|
|
return 'boolean';
|
|
|
|
case '🖹':
|
|
|
|
return 'text';
|
2020-03-05 08:44:32 +01:00
|
|
|
case '🕓':
|
|
|
|
return 'timestamp';
|
2020-02-20 15:22:33 +01:00
|
|
|
default:
|
|
|
|
return '';
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* returns columns and attributes for a table
|
|
|
|
*
|
|
|
|
* @param {string} str table definition
|
|
|
|
* @returns {object} table info
|
2020-02-20 11:09:01 +01:00
|
|
|
*/
|
|
|
|
function get_table_info (str) {
|
|
|
|
const lines = str.split (/\n/ug);
|
|
|
|
lines.splice (0, 2);
|
|
|
|
lines.splice (lines.length - 2, 2);
|
2020-02-20 15:22:33 +01:00
|
|
|
const name_line = lines.shift ();
|
|
|
|
const { name } = (/<b>(?<name>\S+)<\/b>/u).exec (name_line).groups;
|
2020-02-20 11:09:01 +01:00
|
|
|
const columns = [];
|
2020-02-20 15:22:33 +01:00
|
|
|
while (lines.length > 0) {
|
|
|
|
const col = {};
|
|
|
|
const l = lines.shift ();
|
|
|
|
const regex = /<tr><td.*?>(?<props>.*?)<\/td><\/tr>/u;
|
|
|
|
const data = regex.exec (l).groups.props.split (/\s+/gu);
|
|
|
|
if (data.length === 3 || data[0] === '🔑') {
|
|
|
|
const opt = data.shift ();
|
|
|
|
if (opt === '★')
|
|
|
|
col.unique = true;
|
|
|
|
if (opt === '🔑')
|
|
|
|
col.type = 'increments';
|
|
|
|
}
|
|
|
|
|
|
|
|
col.name = data.shift ();
|
|
|
|
if (data.length > 0)
|
|
|
|
col.type = get_type (data.shift ());
|
|
|
|
|
|
|
|
|
|
|
|
columns.push (col);
|
|
|
|
}
|
2020-02-20 11:09:01 +01:00
|
|
|
|
2020-02-21 11:29:50 +01:00
|
|
|
return { name, columns, foreign_keys: [] };
|
2020-02-20 11:09:01 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* get all tables from a structure file
|
|
|
|
*
|
|
|
|
* @param {string} file path to the structure file
|
2020-02-20 15:22:33 +01:00
|
|
|
* @returns {Promise<object>} tables and foreign keys
|
2020-02-20 11:09:01 +01:00
|
|
|
*/
|
|
|
|
async function get_tables (file) {
|
|
|
|
const lines = (await fs.readFile (file, 'utf-8')).split (/\n/gu);
|
|
|
|
const curr = [];
|
|
|
|
const tables = [];
|
2020-02-20 15:22:33 +01:00
|
|
|
const foreign = [];
|
2020-02-20 11:09:01 +01:00
|
|
|
for (const l of lines) {
|
|
|
|
if (curr.length > 0 || (/\S+ \[label=</u).test (l))
|
|
|
|
curr.push (l);
|
|
|
|
|
2020-02-20 15:22:33 +01:00
|
|
|
if ((/>.*?\]/u).test (l)) {
|
2020-02-21 11:29:50 +01:00
|
|
|
const val = curr.join ('\n');
|
|
|
|
if (val)
|
|
|
|
tables.push (get_table_info (val));
|
2020-02-20 11:09:01 +01:00
|
|
|
curr.splice (0, curr.length);
|
|
|
|
}
|
2020-02-20 15:22:33 +01:00
|
|
|
|
2020-02-21 11:29:50 +01:00
|
|
|
get_foreign_key (l, foreign);
|
|
|
|
}
|
2020-02-20 15:22:33 +01:00
|
|
|
|
2020-02-21 11:29:50 +01:00
|
|
|
for (const fk of foreign) {
|
|
|
|
for (let i = 0; i < tables.length; i++) {
|
|
|
|
if (tables[i].name === fk.table) {
|
|
|
|
tables[i].foreign_keys.push (fk);
|
|
|
|
break;
|
|
|
|
}
|
2020-02-20 15:22:33 +01:00
|
|
|
}
|
2020-02-20 11:09:01 +01:00
|
|
|
}
|
|
|
|
|
2020-02-21 11:29:50 +01:00
|
|
|
return tables;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* gets foreign keys from a line
|
|
|
|
*
|
|
|
|
* @param {string} line line to check
|
|
|
|
* @param {Array<object>} foreign_keys array to add to
|
|
|
|
*/
|
|
|
|
function get_foreign_key (line, foreign_keys) {
|
|
|
|
const fk = (/(?<col>\S+) -> (?<ref>\S+)/u).exec (line);
|
|
|
|
if (fk) {
|
|
|
|
const col = fk.groups.col.split (':');
|
|
|
|
const ref = fk.groups.ref.split (':');
|
|
|
|
|
|
|
|
const foreign_key = {
|
|
|
|
table: col[0],
|
|
|
|
column: col[1],
|
|
|
|
ref_table: ref[0],
|
|
|
|
ref_column: ref[1]
|
|
|
|
};
|
|
|
|
|
|
|
|
foreign_keys.push (foreign_key);
|
|
|
|
}
|
2020-02-20 15:22:33 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* creates a function for creating a table
|
|
|
|
*
|
|
|
|
* @param {object} table table to create a function for
|
|
|
|
* @returns {string} function
|
|
|
|
*/
|
|
|
|
function create_table_function (table) {
|
|
|
|
let func = `/**
|
|
|
|
* create table ${table.name}
|
|
|
|
*
|
|
|
|
* @param {any} knex database connection
|
|
|
|
* @returns {Promise} result
|
2020-02-21 11:38:53 +01:00
|
|
|
*/
|
2020-02-20 15:22:33 +01:00
|
|
|
function create_${table.name} (knex) {
|
|
|
|
return knex.schema.createTable ('${table.name}', (table) => {
|
|
|
|
${table.columns
|
2020-02-21 11:29:50 +01:00
|
|
|
.map ((col) => `table.${col.type} ('${col.name}');`)
|
2020-02-20 15:22:33 +01:00
|
|
|
.join ('\n ')}
|
|
|
|
`;
|
|
|
|
|
|
|
|
const unique = table.columns.filter ((val) => val.unique);
|
|
|
|
if (unique.length > 0) {
|
2020-02-21 11:29:50 +01:00
|
|
|
func += `\n table.unique (${unique
|
2020-02-20 15:22:33 +01:00
|
|
|
.map ((val) => `'${val.name}'`)
|
|
|
|
.join (', ')});\n`;
|
|
|
|
}
|
|
|
|
|
2020-02-21 11:29:50 +01:00
|
|
|
if (table.foreign_keys.length > 0) {
|
|
|
|
func += '\n';
|
|
|
|
for (const fk of table.foreign_keys) {
|
|
|
|
func += ` table.foreign ('${fk.column}')
|
|
|
|
.references ('${fk.ref_column}')
|
|
|
|
.inTable ('${fk.ref_table}');\n`;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-02-20 15:22:33 +01:00
|
|
|
func += ` });
|
|
|
|
}`;
|
|
|
|
|
|
|
|
return func;
|
2020-02-20 11:09:01 +01:00
|
|
|
}
|
|
|
|
|
2020-02-21 11:29:50 +01:00
|
|
|
/**
|
|
|
|
* creates the migration function
|
|
|
|
*
|
|
|
|
* @param {Array<object>} tables table array
|
|
|
|
* @returns {string} function
|
|
|
|
*/
|
|
|
|
function create_up_function (tables) {
|
|
|
|
const func = `async function up (knex) {
|
|
|
|
${tables.map ((val) => ` await create_${val.name} (knex);`)
|
|
|
|
.join ('\n')}
|
|
|
|
}`;
|
|
|
|
return func;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* creates the complete migration file for a graph
|
|
|
|
*
|
|
|
|
* @param {string} file_path file to scan
|
|
|
|
* @returns {Promise<string>} file
|
|
|
|
*/
|
|
|
|
async function create_migration (file_path) {
|
|
|
|
const tables = await get_tables (file_path);
|
|
|
|
const functions = tables.map ((tab) => create_table_function (tab));
|
|
|
|
|
2020-02-21 11:38:53 +01:00
|
|
|
const file = `'use strict';
|
|
|
|
|
|
|
|
${functions.join ('\n\n')}
|
2020-02-21 11:29:50 +01:00
|
|
|
|
2020-02-21 11:38:53 +01:00
|
|
|
/**
|
2020-02-21 11:29:50 +01:00
|
|
|
* run migration
|
|
|
|
*
|
|
|
|
* @param {any} knex db connection
|
|
|
|
*/
|
|
|
|
${create_up_function (tables)}
|
|
|
|
|
2020-02-21 11:38:53 +01:00
|
|
|
/**
|
2020-02-21 11:29:50 +01:00
|
|
|
* revert migration
|
|
|
|
*/
|
2020-02-21 11:38:53 +01:00
|
|
|
function down () {
|
|
|
|
// noop
|
|
|
|
}
|
2020-02-21 11:29:50 +01:00
|
|
|
|
2020-02-21 11:38:53 +01:00
|
|
|
module.exports = { up, down };
|
2020-02-21 11:29:50 +01:00
|
|
|
`;
|
|
|
|
return file;
|
|
|
|
}
|
|
|
|
|
|
|
|
module.exports = { get_tables, create_table_function, create_migration };
|