diff --git a/.eslintignore b/.eslintignore deleted file mode 100644 index 6ae540d..0000000 --- a/.eslintignore +++ /dev/null @@ -1 +0,0 @@ -/snippets/ diff --git a/snippets/db/dot_parser.js b/snippets/db/dot_parser.js index cc08c7f..72ea502 100644 --- a/snippets/db/dot_parser.js +++ b/snippets/db/dot_parser.js @@ -1,17 +1,65 @@ +/* eslint-disable no-magic-numbers */ + 'use strict'; const fs = require ('fs-extra'); /** - * @param str + * 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'; + default: + return ''; + } +} + +/** + * returns columns and attributes for a table + * + * @param {string} str table definition + * @returns {object} table info */ function get_table_info (str) { const lines = str.split (/\n/ug); lines.splice (0, 2); lines.splice (lines.length - 2, 2); - const name_line = lines.splice (0); - const { name } = (/(?\S)<\/b>/u).exec (name_line).groups; + const name_line = lines.shift (); + const { name } = (/(?\S+)<\/b>/u).exec (name_line).groups; const columns = []; + while (lines.length > 0) { + const col = {}; + const l = lines.shift (); + const regex = /(?.*?)<\/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); + } return { name, columns }; } @@ -20,23 +68,77 @@ function get_table_info (str) { * get all tables from a structure file * * @param {string} file path to the structure file - * @returns {Array} tables + * @returns {Promise} tables and foreign keys */ async function get_tables (file) { const lines = (await fs.readFile (file, 'utf-8')).split (/\n/gu); const curr = []; const tables = []; + const foreign = []; for (const l of lines) { if (curr.length > 0 || (/\S+ \[label=\]/u).test (l)) { + if ((/>.*?\]/u).test (l)) { tables.push (curr.join ('\n')); curr.splice (0, curr.length); } + + const fk = (/(?\S+) -> (?\S+)/u).exec (l); + 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.push (foreign_key); + } } - return tables.map ((val) => get_table_info (val)); + return { + tables: tables + .filter ((val) => val) + .map ((val) => get_table_info (val)), + foreign_keys: foreign + }; } -module.exports = { get_tables }; +/** + * 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 + */ +function create_${table.name} (knex) { + return knex.schema.createTable ('${table.name}', (table) => { + ${table.columns + .map ((col) => `table.${col.type}('${col.name}');`) + .join ('\n ')} +`; + + const unique = table.columns.filter ((val) => val.unique); + if (unique.length > 0) { + func += `\n table.unique(${unique + .map ((val) => `'${val.name}'`) + .join (', ')});\n`; + } + + func += ` }); +}`; + + return func; +} + +module.exports = { get_tables, create_table_function }; diff --git a/test/db_structure.dot b/test/db_structure.dot new file mode 100644 index 0000000..04ccfbc --- /dev/null +++ b/test/db_structure.dot @@ -0,0 +1,102 @@ +digraph { + node [shape=box,margin=0,height=0] + edge [arrowhead=open,style=dashed] + rankdir="RL"; + + users [label=< + + + + + + + + +
users
🔑 id
★ name ''
★ email ''
password ''
salt ''
deleted ✓
+ >] + + applications [label=< + + + + +
applications
🔑 id
name ''
+ >] + + permissions [label=< + + + + + + + + + + +
permissions
🔑 id
user_id #
create_app ✓
manage_users ✓
manage_permissions ✓
manage_apps ✓
manage_keys ✓
issue_key ✓
+ >] + + user_access [label=< + + + + + + +
user_access
🔑 id
user_id #
app_id #
crud ''
+ >] + + auth_keys [label=< + + + + + + + + +
auth_keys
🔑 id
★ key ''
app_id #
read_data ✓
★ hash ''
name ''
+ >] + + categories [label=< + + + + + +
categories
🔑 id
★ name ''
app_id #
+ >, shape=box, margin=0] + + log [label=< + + + + + + + +
log
🔑 id
key_id #
app_id #
timestamp #
category_id #
+ >] + + data [label=< + + + + + + +
data
🔑 id
log_id #
key ''
value ''
+ >] + + data:log_id -> log:id [color=blue] + permissions:user_id -> users:id [color=blue] + log:key_id -> auth_keys:id [color=blue] + auth_keys:app_id -> applications:id [color=blue] + user_access:user_id -> users:id [color=red] + user_access:app_id -> applications:id [color=red] + log:app_id -> applications:id [color=blue] + log:category_id -> categories:id [color=blue] + categories:app_id -> applications:id [color=blue] +} +data_key [label=key] \ No newline at end of file diff --git a/test/index.js b/test/index.js new file mode 100644 index 0000000..18d281e --- /dev/null +++ b/test/index.js @@ -0,0 +1,14 @@ +/* eslint-disable no-console */ +'use strict'; + +const dot_parser = require ('../snippets/db/dot_parser'); + +/** + * test + */ +async function test () { + const data = await dot_parser.get_tables ('db_structure.dot'); + for (const table of data) + console.log (dot_parser.create_table_function (table)); +} +test ();