/* eslint-disable no-console */ import { existsSync, promises as fsp } from 'fs'; import { Settings, run } from 'graphile-migrate'; import { join, normalize, relative, resolve } from 'path'; import { readdirpPromise } from 'readdirp'; import { DbLogger } from '../common'; /** * Executes all .sql files located in a specified directory. * * @param settings - graphile-migrate settings object * @param dirPath - relative path from the root of a project to a folder with sql scripts in it, e.g. 'migrations/after-reset' */ export const runSqlScripts = async ( settings: Settings, dirPath: string, logger: DbLogger, ): Promise => { const sortedList = ( await readdirpPromise(dirPath, { fileFilter: (entry) => entry.basename.endsWith('.sql'), }) ).sort((a, b) => a.path.localeCompare(b.path)); for await (const { path } of sortedList) { const scriptPath = join(dirPath, path); const content = await fsp.readFile(scriptPath, 'utf8'); logger.debug(`Running Script: '${scriptPath}'`); await run(settings, content, path); } }; /** * Executes a `migrations/current.sql` located in the root of the project using graphile-migrate `run` command * * @param settings - graphile-migrate settings object */ export const runCurrentSql = async ( settings: Settings, logger: DbLogger, ): Promise => { const migrationsFolder = settings.migrationsFolder ?? join(process.cwd(), 'migrations'); const path = join(migrationsFolder, 'current.sql'); const content = await fsp.readFile(path, 'utf8'); logger.debug(`Running Script: '${path}'`); await run(settings, content, path); }; /** * Retrieves an array of relative string paths to before-migration sql scripts, which is consumed by graphile-migrate settings. * Scripts contain helper functions which are added to ax_utils schema. * Approximate return value: * ``` * [ '..\\..\\..\\..\\libs\\service-common\\migrations\\before-migration\\001-setup.sql', '..\\..\\..\\..\\libs\\service-common\\migrations\\before-migration\\002-error-functions.sql', '..\\..\\..\\..\\libs\\service-common\\migrations\\before-migration\\003-validation-functions.sql', '..\\..\\..\\..\\libs\\service-common\\migrations\\before-migration\\004-subscriptions.sql', '..\\..\\..\\..\\libs\\service-common\\migrations\\before-migration\\005-created-updated-triggers.sql', '..\\..\\..\\..\\libs\\service-common\\migrations\\before-migration\\006-authorization.sql', ] ``` */ export const getBeforeMigrationScripts = async ( migrationsFolder?: string, ): Promise => { const executionPath = migrationsFolder ?? join(process.cwd(), 'migrations'); const dirPath = resolve( __dirname, '..', '..', 'migrations', 'before-migration', ); return ( await readdirpPromise(dirPath, { fileFilter: (entry) => entry.basename.endsWith('.sql'), }) ) .sort((a, b) => a.path.localeCompare(b.path)) .map((file) => relative(executionPath, file.fullPath)); }; /** * Retrieve a relative path to watch-fixtures.sql of `graphile-build-pg` package. The path is relative to the 'migrations' folder of a specific project. * Should only be used in development mode. Useful when there is a need to explicitly install postgraphile watch fixtures, so that database owner user can be created without a superuser privilege. * Approximate return value: `../../../../node_modules/graphile-build-pg/res/watch-fixtures.sql` */ export const getGraphileBuildPgWatchFixturesPath = (): string => { let startPath = process.cwd(); let lastPath = ''; // Directory structure is traversed manually, because we want to first check project node_modules, and then repository node_modules. // require.resolve is not useful here, because it will resolve a path to `graphile-build-pg` which is installed in this lib, and not in target project (which can be different). while (lastPath !== startPath) { const lookupPath = join( startPath, 'node_modules', 'graphile-build-pg', 'res', 'watch-fixtures.sql', ); if (existsSync(lookupPath)) { return relative(join(process.cwd(), 'migrations'), lookupPath); } else { // Preserve currently checked path for while condition comparison lastPath = startPath; // Remove last segment from path startPath = normalize(join(startPath, '..')); } } throw Error( '`node_modules\\graphile-build-pg\\res\\watch-fixtures.sql` file not found. Please make sure that `graphile-build-pg` is installed.', ); };