UNPKG

@shagital/adonisjs-crud-generator

Version:

Adonisjs Admin Panel Generator is a package that helps you quickly scaffold your typical CRUD admin interfaces. It generates the admin panel code based on the existing (migrated) table in the database

350 lines (349 loc) 14.9 kB
"use strict"; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.resolveModelName = exports.replaceAll = exports.titleCase = exports.toSnakeCase = exports.validEmail = exports.resolveUrl = exports.exitInColors = exports.random = exports.camelCase = exports.pascalCase = exports.slugify = exports.validateConnection = exports.sqliteColumns = exports.pgsqlColumns = exports.mysqlColumns = exports.getTableColumnsAndTypes = void 0; const fs_extra_1 = __importDefault(require("fs-extra")); const url_1 = __importDefault(require("url")); const supportedTypes = ['mysql', 'sqlite3', 'pg']; const getTableColumnsAndTypes = (dbInstance, Config) => { let cache = {}; return async (tableName, dbConnection = null) => { if (cache[tableName]) { return Promise.resolve(cache[tableName]); } else { let connectionString = dbConnection || Config.get(`database.connection`); let connectionObj = Config.get(`database.connections.${connectionString}`); const conn = dbInstance.connection(connectionString); let columns = []; switch (connectionObj.client) { case 'mysql': columns = await mysqlColumns(conn, tableName, connectionObj.connection.database); break; case 'pg': columns = await pgsqlColumns(conn, tableName); break; case 'sqlite3': columns = await sqliteColumns(conn, tableName); break; default: exitInColors(`Unsupported DB connection [${connectionString}]`); break; } if (Object.keys(columns).length < 1) { exitInColors(`Table [${tableName}] not found`); } cache[tableName] = columns; return columns; } }; }; exports.getTableColumnsAndTypes = getTableColumnsAndTypes; async function mysqlColumns(conn, tableName, dbName) { let columns = []; let queryResult = await conn.rawQuery(` select col.*, case when kcu.referenced_table_schema is null then null else '>-' end as rel, concat(kcu.referenced_table_schema, '.', kcu.referenced_table_name) as primary_table, kcu.referenced_column_name as pk_column_name, kcu.constraint_name as fk_constraint_name from information_schema.columns col join information_schema.tables tab on col.table_schema = tab.table_schema and col.table_name = tab.table_name left join information_schema.key_column_usage kcu on col.table_schema = kcu.table_schema and col.table_name = kcu.table_name and col.column_name = kcu.column_name and kcu.referenced_table_schema is not null where tab.table_type = 'BASE TABLE' and col.table_name = '${tableName}' and col.table_schema = '${dbName}' order by col.table_schema, col.table_name, col.ordinal_position `); Object.keys(queryResult[0]).forEach((key) => { let s = queryResult[0][key]; let primaryTable = (s.primary_table || '').split('.')[1] || null; let primaryColumn = s.pk_column_name; let relationName = (s.fk_constraint_name || '') .replace(tableName, '') .replace('foreign', '') .replace('id', ''); columns[s.COLUMN_NAME] = { type: ['timestamp', 'datetime'].includes(s.DATA_TYPE) ? 'datetime' : ['date'].includes(s.DATA_TYPE) ? 'date' : ['varchar', 'text', 'enum'].includes(s.DATA_TYPE) ? 'string' : s.DATA_TYPE === 'boolean' || s.COLUMN_TYPE === 'tinyint(1)' ? 'boolean' : s.DATA_TYPE.includes('int') || ['decimal'].includes(s.DATA_TYPE) ? 'number' : 'others', unique: ['UNI', 'PRI'].includes(s.COLUMN_KEY), primary: s.COLUMN_KEY === 'PRI', nullable: s.IS_NULLABLE === 'YES', length: s.CHARACTER_MAXIMUM_LENGTH, autoincrement: s.EXTRA === 'auto_increment', primary_table: primaryTable, primary_column: primaryColumn, relation_name: relationName, original_type: s.DATA_TYPE, }; }); return columns; } exports.mysqlColumns = mysqlColumns; async function pgsqlColumns(conn, tableName) { let columns = []; let tableColumnsQuery = await conn.rawQuery(` SELECT * FROM information_schema.columns WHERE table_name = '${tableName}'; `); let tableColumns = tableColumnsQuery.rows; let indexQuery = await conn.rawQuery(` select t.relname as table_name, i.relname as index_name, array_to_string(array_agg(a.attname), ', ') as column_names from pg_class t, pg_class i, pg_index ix, pg_attribute a where t.oid = ix.indrelid and i.oid = ix.indexrelid and a.attrelid = t.oid and a.attnum = ANY(ix.indkey) and t.relkind = 'r' and t.relname like '${tableName}%' group by t.relname, i.relname order by t.relname, i.relname; `); let indexes = indexQuery.rows; let foreignKeysQuery = await conn.rawQuery(` SELECT tc.constraint_name, tc.table_name, kcu.column_name, ccu.table_schema AS foreign_table_schema, ccu.table_name AS foreign_table_name, ccu.column_name AS foreign_column_name FROM information_schema.table_constraints AS tc JOIN information_schema.key_column_usage AS kcu ON tc.constraint_name = kcu.constraint_name AND tc.table_schema = kcu.table_schema JOIN information_schema.constraint_column_usage AS ccu ON ccu.constraint_name = tc.constraint_name AND ccu.table_schema = tc.table_schema WHERE tc.constraint_type = 'FOREIGN KEY' AND tc.table_name='${tableName}'; `); indexes = indexes.concat(foreignKeysQuery.rows); Object.keys(tableColumns).forEach((key) => { let tableColumn = tableColumns[key]; let columnName = tableColumn.column_name; let columnType = tableColumn.data_type.split(' ')[0]; let foreign = foreignKeysQuery.rows.find((s) => s.column_name === columnName); columns[columnName] = { type: ['timestamp', 'datetime'].includes(columnType) ? 'datetime' : ['date'].includes(columnType) ? 'date' : ['varchar', 'text', 'character'].includes(columnType) ? 'string' : columnType.includes('int') || ['decimal', 'enum'].includes(columnType) ? 'number' : columnType === 'boolean' ? 'boolean' : 'others', unique: !!indexes.find((s) => s.column_names === columnName && s.index_name && s.index_name.split('_').slice(-1)[0] === 'unique'), primary: !!indexes.find((s) => s.column_names === columnName && s.index_name && s.index_name.split('_').slice(-1)[0] === 'pkey'), nullable: tableColumn.is_nullable === 'YES', length: tableColumn.numeric_precision || tableColumn.character_maximum_length, autoincrement: tableColumn.column_default && tableColumn.column_default.includes(`${tableColumn.table_name}_${tableColumn.column_name}_seq`), primary_table: foreign ? foreign.foreign_table_name : null, primary_column: foreign ? foreign.foreign_column_name : null, relation_name: foreign ? foreign.constraint_name.replace(tableName, '').replace('foreign', '').replace('id', '') : null, original_type: columnType, }; }); return columns; } exports.pgsqlColumns = pgsqlColumns; async function sqliteColumns(conn, tableName) { let columns = []; let queryResult = await conn.rawQuery(`PRAGMA table_info('${tableName}');`); let foreignKeys = await conn.rawQuery(`PRAGMA foreign_key_list('${tableName}');`); let indexes = await conn.rawQuery(`PRAGMA index_list('${tableName}');`); for (let i in indexes) { indexes[i].indexes = await conn.rawQuery(`PRAGMA index_info('${indexes[i].name}');`); } let pkCheck = await conn.rawQuery(`SELECT COUNT(*) as pk FROM sqlite_sequence WHERE name='${tableName}';`); Object.keys(queryResult).forEach((key) => { let tableColumn = queryResult[key]; let columnName = tableColumn['name']; let splitColumn = tableColumn['type'].split('('); let columnType = splitColumn[0]; let length = splitColumn[1] ? splitColumn[1].split(')')[0] : null; let foreignCol = foreignKeys.find((s) => s.from === columnName); let index = indexes.find((s) => !!s.indexes.find((b) => b.name === columnName)); columns[columnName] = { type: ['timestamp', 'datetime'].includes(columnType) ? 'datetime' : ['date'].includes(columnType) ? 'date' : ['varchar', 'text'].includes(columnType) ? 'string' : columnType.includes('int') || ['decimal', 'enum'].includes(columnType) ? 'number' : columnType === 'boolean' ? 'boolean' : 'others', unique: !!(index && index.unique === 1), primary: tableColumn.pk === 1, nullable: tableColumn.notnull === 0, length: length > 0 ? parseInt(length) : null, autoincrement: pkCheck[0].pk === 0, primary_table: foreignCol ? foreignCol.table : null, primary_column: foreignCol ? foreignCol.to : null, relation_name: foreignCol ? foreignCol.from.replace('_id', '') : null, original_type: columnType, }; }); return columns; } exports.sqliteColumns = sqliteColumns; async function validateConnection(dbInstance, Config, connection) { let dbConnection = Config.get(`database.${connection}`); if (!dbConnection) { exitInColors(`Invalid DB Connection [${connection}]. Ensure the connection is defined in config/database.ts`); } let dbType = dbConnection.client; if (!supportedTypes.includes(dbType)) { exitInColors(`Unsupported DB Type [${dbType}]. Only [${supportedTypes.join(',')}] are supported`); } if (dbType === 'mysql') { try { let dbExist = await dbInstance.connection(connection).raw(`SELECT SCHEMA_NAME FROM INFORMATION_SCHEMA.SCHEMATA WHERE SCHEMA_NAME = '${dbConnection.connection.database}';`); if (dbExist[0][0]['SCHEMA_NAME'] !== dbConnection.connection.database) { exitInColors(`DB [${dbConnection.connection.database}] does not exist`); } } catch (err) { exitInColors(`Failed to connect to DB [${dbConnection.connection.database}]: ${err.toString()}`); } } else if (dbType === 'pg') { try { let dbExist = await dbInstance.connection(connection).raw(` SELECT datname FROM pg_catalog.pg_database WHERE lower(datname) = lower('${dbConnection.connection.database}'); `); if (!dbExist['rows'][0] || dbExist['rows'][0]['datname'] !== dbConnection.connection.database) { exitInColors(`DB [${dbConnection.connection.database}] does not exist`); } } catch (err) { exitInColors(`Failed to connect to DB [${dbConnection.connection.database}]: ${err.toString()}`); } } else { let exists = false; try { if (fs_extra_1.default.existsSync(dbConnection.connection.filename)) { exists = true; } } catch (err) { exists = false; } finally { if (!exists) { exitInColors(`Failed to connect to DB [${dbConnection.connection.filename}]`); } } } } exports.validateConnection = validateConnection; function slugify(text) { return text .toLowerCase() .replace(/ /g, '-') .replace(/[^\w-]+/g, ''); } exports.slugify = slugify; function pascalCase(string) { return `${string}` .replace(new RegExp(/[-_]+/, 'g'), ' ') .replace(new RegExp(/[^\w\s]/, 'g'), '') .replace(new RegExp(/\s+(.)(\w+)/, 'g'), (_$1, $2, $3) => `${$2.toUpperCase() + $3.toLowerCase()}`) .replace(new RegExp(/\s/, 'g'), '') .replace(new RegExp(/\w/), (s) => s.toUpperCase()); } exports.pascalCase = pascalCase; function camelCase(str) { let pascal = pascalCase(str); let firsChar = pascal.charAt(0).toLowerCase(); return firsChar + pascal.substring(1); } exports.camelCase = camelCase; function random(length = 10) { let result = ''; let characters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789'; let charactersLength = characters.length; for (let i = 0; i < length; i++) { result += characters.charAt(Math.floor(Math.random() * charactersLength)); } return result; } exports.random = random; function exitInColors(message) { console.log('\x1b[41m', message, '\x1b[0m'); process.exit(1); } exports.exitInColors = exitInColors; function resolveUrl(fullUrl, property) { let parsed = url_1.default.parse(fullUrl); return parsed[property]; } exports.resolveUrl = resolveUrl; function validEmail(emailToValidate) { const emailRegexp = /^[a-zA-Z0-9.!#$%&'*+/=?^_`{|}~-]+@[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*$/; return emailRegexp.test(emailToValidate); } exports.validEmail = validEmail; function toSnakeCase(string) { string = String(string); return string .replace(/\W+/g, ' ') .split(/ |\B(?=[A-Z])/) .map((word) => word.toLowerCase()) .join('_'); } exports.toSnakeCase = toSnakeCase; function titleCase(str) { str = String(str); return str.replace(/_/g, ' ').replace(/\w\S*/g, function (word) { return word.charAt(0).toUpperCase() + word.substr(1).toLowerCase(); }); } exports.titleCase = titleCase; function replaceAll(str, find, replace) { return str.replace(new RegExp(find, 'g'), replace); } exports.replaceAll = replaceAll; function resolveModelName(namespace) { return namespace.split('/').pop(); } exports.resolveModelName = resolveModelName;