trailsjs/trailpack-sequelize

View on GitHub
index.js

Summary

Maintainability
A
1 hr
Test Coverage
/* eslint no-console: [0] */
'use strict'

const Trailpack = require('trailpack/datastore')
const lib = require('./lib')
const _ = require('lodash')

module.exports = class SequelizeTrailpack extends Trailpack {

  /**
   * Validate the database config, and api.model definitions
   */
  async validate() {
    const stores = this.app.config.get('stores')
    const models = this.app.config.get('models')

    if (stores && Object.keys(stores).length === 0) {
      this.app.config.log.logger.warn('No store configured at config.stores, models will be ignored')
    }

    return Promise.all([
      lib.Validator.validateStoresConfig(stores),
      lib.Validator.validateModelsConfig(models)
    ])
  }

  /**
   * Merge configuration into models, load Sequelize collections.
   */
  configure() {
    this.app.config.set('stores.orm', 'sequelize')
    _.merge(this.app.config, lib.FailsafeConfig)
  }

  /**
   * Initialize Sequelize. This will compile the schema and connect to the
   * database.
   */
  initialize() {
    super.initialize()

    this.orm = this.orm || {}
    this.app.orm = {}
    this.connections = lib.Transformer.transformStoreConnections(this.app)
    this.app.models = lib.Transformer.transformModels(this.app)

    this.stores = _.mapValues(this.app.config.get('stores'), (store, storeName) => {
      return {
        models: _.pickBy(this.app.models, { store: storeName }),
        connection: this.connections[storeName],
        migrate: store.migrate || this.app.config.get('models.migrate')
      }
    })

    // Loop through store models and define them in Sequelize
    _.each(this.stores, (store, storeName) => {
      _.each(store.models, (model, modelName) => {
        const Model = store.connection.define(modelName, model.schema, model.options)

        if (model.options) {
          if (model.options.classMethods) {
            for (const methodName in model.options.classMethods) {
              Model[methodName] = model.options.classMethods[methodName]
            }
          }

          if (model.options.instanceMethods) {
            for (const methodName in model.options.instanceMethods) {
              Model.prototype[methodName] = model.options.instanceMethods[methodName]
            }
          }
        }
        this.app.orm[model.globalId] = Model
      })
    })

    // Loop through store models and associate
    _.each(this.stores, (store, storeName) => {
      // Run Associate on the Models
      _.each(store.models, (model, modelName) => {
        // ignore model if not configured
        if (!this.app.orm[model.globalId]) {
          return
        }
        // Associate models if method defined
        if (this.app.orm[model.globalId].associate) {
          this.app.orm[model.globalId].associate(this.app.orm)
        }

        // Reset the orm Model
        this.orm[model.globalId.toLowerCase()] = this.app.orm[model.globalId]
      })
    })

    return this.migrate()
  }

  /**
   * Close all database connections
   */
  async unload() {
    return Promise.all(
      _.map(this.stores, store => {
        return new Promise((resolve, reject) => {
          store.connection.close()
          resolve()
        })
      })
    )
  }

  async migrate() {
    const SchemaMigrationService = this.app.services.SchemaMigrationService

    return Promise.all(
      _.map(this.stores, store => {
        if (store.migrate === 'drop') {
          return SchemaMigrationService.dropDB(store.connection)
        }
        else if (store.migrate === 'alter') {
          return SchemaMigrationService.alterDB(store.connection)
        }
        else if (store.migrate === 'none') {
          return
        }
        else {
          return
        }
      })
    )
  }

  constructor(app) {
    super(app, {
      config: require('./config'),
      api: require('./api'),
      pkg: require('./package')
    })
  }
}