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