UNPKG

@resin/pinejs

Version:

Pine.js is a sophisticated rules-driven API engine that enables you to define rules in a structured subset of English. Those rules are used in order for Pine.js to generate a database schema and the associated [OData](http://www.odata.org/) API. This make

106 lines (89 loc) 2.97 kB
_ = require 'lodash' Promise = require 'bluebird' TypedError = require 'typed-error' modelText = require './migrations.sbvr' permissions = require '../sbvr-api/permissions' exports.MigrationError = class MigrationError extends TypedError exports.run = (tx, model) -> if not _.some(model.migrations) return Promise.fulfilled() modelName = model.apiRoot # migrations only run if the model has been executed before, # to make changes that can't be automatically applied @checkModelAlreadyExists(tx, modelName) .then (exists) => if not exists @logger.info('First time model has executed, skipping migrations') return @setExecutedMigrations(tx, modelName, _.keys(model.migrations)) @getExecutedMigrations(tx, modelName) .then (executedMigrations) => pendingMigrations = @filterAndSortPendingMigrations(model.migrations, executedMigrations) return if not _.some(pendingMigrations) @executeMigrations(tx, pendingMigrations) .then (newlyExecutedMigrations) => @setExecutedMigrations(tx, modelName, [ executedMigrations..., newlyExecutedMigrations... ]) exports.checkModelAlreadyExists = (tx, modelName) -> @sbvrUtils.api.dev.get resource: 'model' passthrough: tx: tx req: permissions.rootRead options: $select: 'is_of__vocabulary' $top: 1 $filter: is_of__vocabulary: modelName .then(_.some) exports.getExecutedMigrations = (tx, modelName) -> @migrationsApi.get resource: 'migration' id: modelName passthrough: tx: tx req: permissions.rootRead options: $select: 'executed_migrations' .then (data) -> data?.executed_migrations || [] exports.setExecutedMigrations = (tx, modelName, executedMigrations) -> @migrationsApi.put resource: 'migration' id: modelName passthrough: tx: tx req: permissions.root body: model_name: modelName executed_migrations: executedMigrations # turns {"key1": migration, "key3": migration, "key2": migration} # into [["key1", migration], ["key2", migration], ["key3", migration]] exports.filterAndSortPendingMigrations = (migrations, executedMigrations) -> _(migrations) .omit(executedMigrations) .toPairs() .sortBy(_.head) .value() exports.executeMigrations = (tx, migrations = []) -> Promise.mapSeries(migrations, @executeMigration.bind(this, tx)) .catch (err) => @logger.error('Error while executing migrations, rolled back') throw new MigrationError(err) .return(_.map(migrations, _.head)) # return migration keys exports.executeMigration = (tx, [ key, migration ]) -> @logger.info "Running migration #{JSON.stringify key}" switch typeof migration when 'function' migration(tx, @sbvrUtils) when 'string' tx.executeSql(migration) exports.config = models: [ modelName: 'migrations' apiRoot: 'migrations' modelText: modelText customServerCode: exports ] exports.setup = (app, @sbvrUtils, db, callback) -> @migrationsApi = @sbvrUtils.api.migrations @logger = @migrationsApi.logger callback()