TryGhost/Ghost

View on GitHub
ghost/core/core/server/data/migrations/utils/tables.js

Summary

Maintainability
A
25 mins
Test Coverage
const logging = require('@tryghost/logging');
const {commands} = require('../../schema');
const {createIrreversibleMigration, createNonTransactionalMigration} = require('./migrations');

/**
 * Creates a migrations which will add a new table from schema.js to the database
 * @param {string} name - table name
 * @param {Object} tableSpec - copy of table schema definition as defined in schema.js at the moment of writing the migration,
 * this parameter MUST be present, otherwise @daniellockyer will hunt you down
 *
 * @returns {Object} migration object returning config/up/down properties
 */
function addTable(name, tableSpec) {
    return createNonTransactionalMigration(
        async function up(connection) {
            const tableExists = await connection.schema.hasTable(name);
            if (tableExists) {
                logging.warn(`Skipping adding table: ${name} - table already exists`);
                return;
            }

            logging.info(`Adding table: ${name}`);
            return commands.createTable(name, connection, tableSpec);
        },
        async function down(connection) {
            const tableExists = await connection.schema.hasTable(name);
            if (!tableExists) {
                logging.warn(`Skipping dropping table: ${name} - table does not exist`);
                return;
            }

            logging.info(`Dropping table: ${name}`);
            return commands.deleteTable(name, connection);
        }
    );
}

/**
 * Creates migration which will drop a table
 *
 * @param {string[]} names  - names of the tables to drop
 */
function dropTables(names) {
    return createIrreversibleMigration(
        async function up(connection) {
            for (const name of names) {
                const exists = await connection.schema.hasTable(name);

                if (!exists) {
                    logging.warn(`Skipping dropping table: ${name} - table does not exist`);
                } else {
                    logging.info(`Dropping table: ${name}`);
                    await commands.deleteTable(name, connection);
                }
            }
        }
    );
}

/**
 * Creates a migration which will drop an existing table and then re-add a new table based on provided spec
 * @param {string} name - table name
 * @param {Object} tableSpec - copy of table schema definition as defined in schema.js at the moment of writing the migration,
 * this parameter MUST be present, otherwise @daniellockyer will hunt you down
 *
 * @returns {Object} migration object returning config/up/down properties
 */
function recreateTable(name, tableSpec) {
    return createNonTransactionalMigration(
        async function up(connection) {
            const exists = await connection.schema.hasTable(name);

            if (!exists) {
                logging.warn(`Skipping dropping table: ${name} - table does not exist`);
            } else {
                logging.info(`Dropping table: ${name}`);
                await commands.deleteTable(name, connection);
                logging.info(`Re-adding table: ${name}`);
                await commands.createTable(name, connection, tableSpec);
            }
        },
        async function down() {
            // noop: we cannot go back to old table schema
            logging.warn(`Ignoring rollback for table recreate: ${name}`);
            return Promise.resolve();
        }
    );
}

module.exports = {
    addTable,
    dropTables,
    recreateTable
};