You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
lib/src/orm/services/Migrations.ts

87 lines
3.1 KiB

import {Inject, Singleton} from '../../di'
import {CanonicalInstantiable} from '../../service/CanonicalInstantiable'
import {Migration} from '../migrations/Migration'
import {CanonicalDefinition, CanonicalResolver} from '../../service/Canonical'
import {UniversalPath} from '../../util'
import {lib} from '../../lib'
import {CommandLine} from '../../cli'
/**
* Service unit that loads and instantiates migration classes.
*/
@Singleton()
export class Migrations extends CanonicalInstantiable<Migration> {
@Inject()
protected readonly cli!: CommandLine
protected appPath = ['migrations']
protected canonicalItem = 'migration'
protected suffix = '.migration'
async up(): Promise<void> {
if ( await this.path.exists() ) {
await super.up()
} else {
this.logging.debug(`Base migration path does not exist, or has no files: ${this.path}`)
}
// Register the migrations for @extollo/lib
const basePath = lib().concat('migrations')
const resolver = await this.buildMigrationNamespaceResolver('@extollo', basePath)
this.registerNamespace('@extollo', resolver)
}
async initCanonicalItem(definition: CanonicalDefinition): Promise<Migration> {
const instance = await super.initCanonicalItem(definition)
if ( !(instance instanceof Migration) ) {
throw new TypeError(`Invalid migration: ${definition.originalName}. Migrations must extend from @extollo/lib.Migration.`)
}
instance.setMigrationIdentifier(definition.canonicalName)
return instance
}
/**
* Creates a CanonicalResolver for a directory that contains migration files.
*
* @example
* ```ts
* const path = universalPath('path', 'to', 'migrations', 'folder')
* const namespace = '@mypackage'
*
* const resolver = await migrations.buildMigrationNamespaceResolver(namespace, path)
* migrations.registerNamespace(namespace, resolver)
* ```
* @param name
* @param basePath
*/
public async buildMigrationNamespaceResolver(name: string, basePath: UniversalPath): Promise<CanonicalResolver<Migration>> {
if ( !name.startsWith('@') ) {
name = `@${name}`
}
const namespace: {[key: string]: Migration} = {}
for await ( const entry of basePath.walk() ) {
if ( !this.isValidSuffix(entry) ) {
this.logging.debug(`buildMigrationNamespaceResolver - Skipping file with invalid suffix: ${entry}`)
continue
}
const definition = await this.buildCanonicalDefinition(entry, basePath)
this.logging.verbose(`buildMigrationNamespaceResolver - Discovered canonical ${this.canonicalItem} "${definition.canonicalName}" from ${entry}`)
namespace[definition.canonicalName] = await this.initCanonicalItem(definition)
namespace[definition.canonicalName].setMigrationIdentifier(`${name}:${namespace[definition.canonicalName].identifier}`)
}
return {
get: (key: string) => namespace[key],
all: () => Object.keys(namespace),
}
}
}