UNPKG

forest-cli

Version:

The Lumberjacks' toolbelt is the Forest CLI which makes easy to manage your back office application directly from the terminal.

393 lines 40.1 kB
const { inject } = require('@forestadmin/context'); const P = require('bluebird'); const { plural, singular } = require('pluralize'); const ColumnTypeGetter = require('./sequelize-column-type-getter'); const DefaultValueExpression = require('./sequelize-default-value'); const TableConstraintsGetter = require('./sequelize-table-constraints-getter'); const EmptyDatabaseError = require('../../../../errors/database/empty-database-error'); const { isUnderscored } = require('../../../../utils/fields'); const ASSOCIATION_TYPE_BELONGS_TO = 'belongsTo'; const ASSOCIATION_TYPE_BELONGS_TO_MANY = 'belongsToMany'; const ASSOCIATION_TYPE_HAS_MANY = 'hasMany'; const ASSOCIATION_TYPE_HAS_ONE = 'hasOne'; const FOREIGN_KEY = 'FOREIGN KEY'; /** Queries database for default schema name */ async function getDefaultSchema(connection, userProvidedSchema) { if (userProvidedSchema) { return userProvidedSchema; } const dialect = connection.getDialect(); const queries = { mssql: 'SELECT SCHEMA_NAME() AS default_schema', mysql: 'SELECT DATABASE() AS default_schema', mariadb: 'SELECT DATABASE() AS default_schema', postgres: 'SELECT CURRENT_SCHEMA() AS default_schema', }; if (queries[dialect]) { const rows = await connection.query(queries[dialect], { type: connection.QueryTypes.SELECT }); return rows.length && rows[0].default_schema ? rows[0].default_schema : 'public'; } return 'public'; } /** Retrieve the description of the fields in a given table. */ async function analyzeFields(queryInterface, tableName, config) { const dialect = queryInterface.sequelize.getDialect(); let columnsByName; // Workaround bug in sequelize/dialects/mysql/query-generator#describe // => Don't provide the schema when using mysql/mariadb if (['mysql', 'mariadb'].includes(dialect)) { columnsByName = await queryInterface.describeTable(tableName, {}); } else { columnsByName = await queryInterface.describeTable(tableName, { schema: config.dbSchema }); } // Workaround bug in sequelize/dialects/(postgres|mssql)/query.js#run() // => Fetch the unmodified default value from the information schema if (dialect === 'postgres' || dialect === 'mssql') { const getDefaultsQuery = ` SELECT column_name as colname, column_default as coldefault FROM information_schema.columns WHERE table_schema = ? AND table_name = ? `; const rows = await queryInterface.sequelize.query(getDefaultsQuery, { type: queryInterface.sequelize.QueryTypes.SELECT, replacements: [config.dbSchema, tableName], }); rows.forEach(row => { columnsByName[row.colname].defaultValue = row.coldefault; }); } Object.values(columnsByName).forEach(column => { const defaultValue = new DefaultValueExpression(dialect, column.type, column.defaultValue); // eslint-disable-next-line no-param-reassign column.defaultValue = defaultValue.parse(); }); return columnsByName; } async function analyzePrimaryKeys(schema) { return Object.keys(schema).filter(column => schema[column].primaryKey); } /** Retrieve table names from the provided schema. */ async function showAllTables(queryInterface, databaseConnection, schema) { const dbDialect = databaseConnection.getDialect(); if (['mysql', 'mariadb'].includes(dbDialect)) { return queryInterface.showAllTables(); } const tables = await queryInterface.sequelize.query("SELECT table_name as table_name FROM INFORMATION_SCHEMA.TABLES WHERE table_schema = ? AND table_type LIKE '%TABLE' AND table_name != 'spatial_ref_sys'", { type: queryInterface.sequelize.QueryTypes.SELECT, replacements: [schema] }); return tables.map(table => table.table_name); } function hasTimestamps(fields) { let hasCreatedAt = false; let hasUpdatedAt = false; fields.forEach(field => { if (field.name === 'createdAt') { hasCreatedAt = true; } if (field.name === 'updatedAt') { hasUpdatedAt = true; } }); return hasCreatedAt && hasUpdatedAt; } function formatAliasName(columnName) { const { assertPresent, lodash } = inject(); assertPresent({ lodash }); const alias = lodash.camelCase(columnName); if (alias.endsWith('Id') && alias.length > 2) { return alias.substring(0, alias.length - 2); } if (alias.endsWith('Uuid') && alias.length > 4) { return alias.substring(0, alias.length - 4); } return alias; } // NOTICE: Look for the id column in both fields and primary keys. function hasIdColumn(fields, primaryKeys) { return (fields.some(field => field.name === 'id' || field.nameColumn === 'id') || primaryKeys?.includes('id')); } function isTechnicalTimestamp({ type, name }) { // NOTICE: Ignore technical timestamp fields. const FIELDS_TO_IGNORE = [ 'createdAt', 'updatedAt', 'deletedAt', 'createDate', 'updateDate', 'deleteDate', 'creationDate', 'deletionDate', ]; return type === 'DATE' && FIELDS_TO_IGNORE.includes(name); } function isJunctionTable(fields, constraints) { for (let index = 0; index < fields.length; index += 1) { const field = fields[index]; // NOTICE: The only fields accepted are primary keys, technical timestamps and foreignKeys if (!isTechnicalTimestamp(field) && !field.primaryKey) { return false; } } const foreignKeys = constraints.filter(constraint => constraint.foreignTableName && constraint.columnName && constraint.columnType === FOREIGN_KEY); // NOTICE: To be a junction table it means you have 2 foreignKeys, no more no less return foreignKeys.length === 2; } // NOTICE: Check the foreign key's reference unicity function checkUnicity(primaryKeys, uniqueIndexes, columnName) { const { assertPresent, lodash } = inject(); assertPresent({ lodash }); const isUnique = uniqueIndexes !== null && uniqueIndexes.find(indexColumnName => indexColumnName.length === 1 && indexColumnName.includes(columnName)); const isPrimary = lodash.isEqual([columnName], primaryKeys); return isPrimary || isUnique; } function associationNameAlreadyExists(existingReferences, newReference) { return existingReferences.some(reference => reference && reference.as === newReference.as); } function referenceAlreadyExists(existingReferences, newReference) { return existingReferences.some(reference => reference && reference.ref === newReference.ref && reference.association === newReference.association && reference.foreignKey === newReference.foreignKey); } // NOTICE: Format the references depending on the type of the association function createReference(tableName, existingsReferences, association, foreignKey, manyToManyForeignKey) { const { assertPresent, lodash, strings } = inject(); assertPresent({ lodash, strings }); const foreignKeyName = lodash.camelCase(foreignKey.columnName); const reference = { foreignKey: foreignKey.columnName, foreignKeyName: `${foreignKeyName}Key`, association, }; if (association === ASSOCIATION_TYPE_BELONGS_TO) { reference.ref = foreignKey.foreignTableName; reference.as = formatAliasName(foreignKey.columnName); if (foreignKey.foreignColumnName !== 'id') { reference.targetKey = foreignKey.foreignColumnName; } } else if (association === ASSOCIATION_TYPE_BELONGS_TO_MANY) { reference.ref = manyToManyForeignKey.foreignTableName; reference.otherKey = manyToManyForeignKey.columnName; reference.through = lodash.camelCase(strings.transformToSafeString(foreignKey.tableName)); reference.as = lodash.camelCase(plural(`${manyToManyForeignKey.foreignTableName}_through_${foreignKey.tableName}`)); } else { reference.ref = foreignKey.tableName; const formater = association === ASSOCIATION_TYPE_HAS_MANY ? plural : singular; const prefix = singular(tableName) === formatAliasName(foreignKeyName) ? '' : `${formatAliasName(foreignKeyName)}_`; if (foreignKey.foreignColumnName !== 'id') { reference.sourceKey = foreignKey.foreignColumnName; } reference.as = lodash.camelCase(formater(`${prefix}${foreignKey.tableName}`)); } if (referenceAlreadyExists(existingsReferences, reference)) return null; if (associationNameAlreadyExists(existingsReferences, reference)) { reference.as = lodash.camelCase(`${reference.as} ${reference.foreignKey}`); } return reference; } async function analyzeTable(queryInterface, tableConstraintsGetter, table, config) { const schema = await analyzeFields(queryInterface, table, config); return { schema, constraints: await tableConstraintsGetter.perform(table), primaryKeys: await analyzePrimaryKeys(schema), }; } function createBelongsToReference(referenceTable, tableReferences, constraint) { const referenceColumnName = constraint.foreignColumnName; const referencePrimaryKeys = referenceTable.primaryKeys; const referenceUniqueConstraint = referenceTable.constraints.find(({ columnType }) => ['UNIQUE', 'PRIMARY KEY'].includes(columnType)); const referenceUniqueIndexes = referenceUniqueConstraint ? referenceUniqueConstraint.uniqueIndexes : null; const isReferencePrimaryOrUnique = checkUnicity(referencePrimaryKeys, referenceUniqueIndexes, referenceColumnName); if (isReferencePrimaryOrUnique) { return createReference(null, tableReferences, ASSOCIATION_TYPE_BELONGS_TO, constraint); } return null; } // NOTICE: Use the foreign key and reference properties to determine the associations // and push them as references of the table. function createAllReferences(databaseSchema, schemaGenerated) { const references = {}; Object.keys(databaseSchema).forEach(tableName => { references[tableName] = []; }); Object.keys(databaseSchema).forEach(tableName => { const table = databaseSchema[tableName]; const { constraints, primaryKeys } = table; const { isJunction } = schemaGenerated[tableName].options; const foreignKeysWithExistingTable = constraints.filter(constraint => constraint.columnType === FOREIGN_KEY && databaseSchema[constraint.foreignTableName]); foreignKeysWithExistingTable.forEach(constraint => { const { columnName } = constraint; const uniqueIndexes = constraint.uniqueIndexes || null; const isPrimaryOrUnique = checkUnicity(primaryKeys, uniqueIndexes, columnName); const referenceTableName = constraint.foreignTableName; if (isJunction) { const manyToManyKeys = foreignKeysWithExistingTable.filter(otherKey => otherKey.columnName !== constraint.columnName); manyToManyKeys.forEach(manyToManyKey => { references[referenceTableName].push(createReference(referenceTableName, references[referenceTableName], ASSOCIATION_TYPE_BELONGS_TO_MANY, constraint, manyToManyKey)); }); } else { references[referenceTableName].push(createReference(referenceTableName, references[referenceTableName], isPrimaryOrUnique ? ASSOCIATION_TYPE_HAS_ONE : ASSOCIATION_TYPE_HAS_MANY, constraint)); } references[tableName].push(createBelongsToReference(databaseSchema[referenceTableName], references[tableName], constraint)); }); }); // remove null references return Object.entries(references).reduce((accumulator, [tableName, tableReferences]) => { accumulator[tableName] = tableReferences.filter(Boolean); return accumulator; }, {}); } function isOnlyJoinTableWithId(schema, constraints) { const idColumn = Object.keys(schema).find(columnName => columnName === 'id'); if (!idColumn) return false; const possibleForeignColumnNames = Object.keys(schema).filter(columnName => !isTechnicalTimestamp(schema[columnName]) && columnName !== 'id'); const columnWithoutForeignKey = possibleForeignColumnNames.find(columnName => !constraints.find(constraint => constraint.columnName === columnName && constraint.columnType === FOREIGN_KEY)); return !columnWithoutForeignKey; } async function createTableSchema(columnTypeGetter, { schema, constraints, primaryKeys }, tableName) { const { assertPresent, lodash } = inject(); assertPresent({ lodash }); const fields = []; await P.each(Object.keys(schema), async (columnName) => { const columnInfo = schema[columnName]; const type = await columnTypeGetter.perform(columnInfo, columnName, tableName); const foreignKey = constraints.find(constraint => constraint.columnName === columnName && constraint.columnType === FOREIGN_KEY); const isValidField = type && (!foreignKey || !foreignKey.foreignTableName || !foreignKey.columnName || columnInfo.primaryKey); // NOTICE: If the column is of integer type, named "id" and primary, Sequelize will handle it // automatically without necessary declaration. const isIdIntegerPrimaryColumn = columnName === 'id' && ['INTEGER', 'BIGINT'].includes(type) && columnInfo.primaryKey; // NOTICE: But in some cases we want to force the id to be still generated. // For example, Sequelize will not use a default id field on a model // that has only foreign keys, so if the id primary key is present, we need to force it. const forceIdColumn = isIdIntegerPrimaryColumn && isOnlyJoinTableWithId(schema, constraints); if (isValidField && (!isIdIntegerPrimaryColumn || forceIdColumn)) { // NOTICE: sequelize considers column name with parenthesis as raw Attributes // do not try to camelCase the name for avoiding sequelize issues const hasParenthesis = columnName.includes('(') || columnName.includes(')'); const name = hasParenthesis ? columnName : lodash.camelCase(columnName); let isRequired = !columnInfo.allowNull; if (isTechnicalTimestamp({ name, type })) { isRequired = false; } const field = { name, nameColumn: columnName, type, primaryKey: columnInfo.primaryKey, defaultValue: columnInfo.defaultValue, isRequired, }; fields.push(field); } }); const options = { underscored: isUnderscored(fields), timestamps: hasTimestamps(fields), hasIdColumn: hasIdColumn(fields, primaryKeys), hasPrimaryKeys: Boolean(primaryKeys?.length), isJunction: isJunctionTable(fields, constraints), }; return { fields, primaryKeys, options, }; } // NOTICE: This detects two generated fields (regular or reference's alias) with the same name // and rename reference's alias as `Linked${collectionReferenced}` to prevent Sequelize // from crashing at startup. function fixAliasConflicts(wholeSchema) { const { assertPresent, lodash } = inject(); assertPresent({ lodash }); const tablesName = Object.keys(wholeSchema); if (!tablesName.length) { return; } tablesName.forEach(tableName => { const table = wholeSchema[tableName]; if (table.references.length && table.fields.length) { const fieldNames = table.fields.map(field => field.name); table.references.forEach((reference, index) => { if (fieldNames.includes(reference.as)) { table.references[index].as = `linked${lodash.upperFirst(reference.as)}`; } }); } }); } async function analyzeSequelizeTables(connection, config, allowWarning) { const { assertPresent, lodash } = inject(); assertPresent({ lodash }); // User provided a schema, check if it exists if (config.dbSchema) { const schemas = await connection.query('SELECT SCHEMA_NAME FROM INFORMATION_SCHEMA.SCHEMATA WHERE SCHEMA_NAME = ?;', { type: connection.QueryTypes.SELECT, replacements: [config.dbSchema] }); if (!schemas.length) { const message = 'This schema does not exists.'; return inject().terminator.terminate(1, { errorCode: 'database_authentication_error', errorMessage: message, logs: [message], }); } } // If dbSchema was not provided by user, get default one. const configWithSchema = { ...config, dbSchema: await getDefaultSchema(connection, config.dbSchema), }; // Build the db schema. const schemaAllTables = {}; const queryInterface = connection.getQueryInterface(); const databaseSchema = {}; const tableNames = await showAllTables(queryInterface, connection, configWithSchema.dbSchema); const constraintsGetter = new TableConstraintsGetter(connection, configWithSchema.dbSchema); await P.each(tableNames, async (tableName) => { const { schema, constraints, primaryKeys } = await analyzeTable(queryInterface, constraintsGetter, tableName, configWithSchema); databaseSchema[tableName] = { schema, constraints, primaryKeys, references: [], }; }); const columnTypeGetter = new ColumnTypeGetter(connection, configWithSchema.dbSchema, allowWarning); await P.each(tableNames, async (tableName) => { schemaAllTables[tableName] = await createTableSchema(columnTypeGetter, databaseSchema[tableName], tableName); }); // NOTICE: Fill the references field for each table schema const referencesPerTable = createAllReferences(databaseSchema, schemaAllTables); Object.keys(referencesPerTable).forEach(tableName => { schemaAllTables[tableName].references = lodash.sortBy(referencesPerTable[tableName], 'association'); // NOTE: When a table contains no field, it will be considered camelCased // by default, so we need to check its references to ensure whether // it is camelCased or not. if (!schemaAllTables[tableName].fields.length) { schemaAllTables[tableName].options.underscored = isUnderscored(schemaAllTables[tableName].references.map(({ foreignKey }) => ({ nameColumn: foreignKey }))); } }); if (Object.keys(schemaAllTables).length === 0) { throw new EmptyDatabaseError('no tables found', { orm: 'sequelize', dialect: connection.getDialect(), }); } fixAliasConflicts(schemaAllTables); return schemaAllTables; } module.exports = analyzeSequelizeTables; //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoic2VxdWVsaXplLXRhYmxlcy1hbmFseXplci5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uLy4uLy4uLy4uL3NyYy9zZXJ2aWNlcy9zY2hlbWEvdXBkYXRlL2FuYWx5emVyL3NlcXVlbGl6ZS10YWJsZXMtYW5hbHl6ZXIuanMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUEsTUFBTSxFQUFFLE1BQU0sRUFBRSxHQUFHLE9BQU8sQ0FBQyxzQkFBc0IsQ0FBQyxDQUFDO0FBQ25ELE1BQU0sQ0FBQyxHQUFHLE9BQU8sQ0FBQyxVQUFVLENBQUMsQ0FBQztBQUM5QixNQUFNLEVBQUUsTUFBTSxFQUFFLFFBQVEsRUFBRSxHQUFHLE9BQU8sQ0FBQyxXQUFXLENBQUMsQ0FBQztBQUNsRCxNQUFNLGdCQUFnQixHQUFHLE9BQU8sQ0FBQyxnQ0FBZ0MsQ0FBQyxDQUFDO0FBQ25FLE1BQU0sc0JBQXNCLEdBQUcsT0FBTyxDQUFDLDJCQUEyQixDQUFDLENBQUM7QUFDcEUsTUFBTSxzQkFBc0IsR0FBRyxPQUFPLENBQUMsc0NBQXNDLENBQUMsQ0FBQztBQUMvRSxNQUFNLGtCQUFrQixHQUFHLE9BQU8sQ0FBQyxrREFBa0QsQ0FBQyxDQUFDO0FBQ3ZGLE1BQU0sRUFBRSxhQUFhLEVBQUUsR0FBRyxPQUFPLENBQUMsMEJBQTBCLENBQUMsQ0FBQztBQUU5RCxNQUFNLDJCQUEyQixHQUFHLFdBQVcsQ0FBQztBQUNoRCxNQUFNLGdDQUFnQyxHQUFHLGVBQWUsQ0FBQztBQUN6RCxNQUFNLHlCQUF5QixHQUFHLFNBQVMsQ0FBQztBQUM1QyxNQUFNLHdCQUF3QixHQUFHLFFBQVEsQ0FBQztBQUUxQyxNQUFNLFdBQVcsR0FBRyxhQUFhLENBQUM7QUFFbEMsK0NBQStDO0FBQy9DLEtBQUssVUFBVSxnQkFBZ0IsQ0FBQyxVQUFVLEVBQUUsa0JBQWtCO0lBQzVELElBQUksa0JBQWtCLEVBQUU7UUFDdEIsT0FBTyxrQkFBa0IsQ0FBQztLQUMzQjtJQUVELE1BQU0sT0FBTyxH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