Manual Reference Source Test

src/middleware/Database.js

// Import the necessary modules.
// @flow
import mongoose from 'mongoose'
import { existsSync } from 'fs'
import {
  isAbsolute,
  join
} from 'path'
import { URL } from 'url'

import { executeCommand } from '../utils'

/**
 * Class for setting up a database connection.
 * @type {Database}
 */
export default class Database {

  /**
   * The name of the database. Default is the package name with the
   * environment mode.
   * @type {string}
   */
  database: string

  /**
   * The host of the server of the database. Default is `['localhost']`.
   * @type {Array<string>}
   */
  hosts: Array<string>

  /**
   * The port of the database. Default is `27017`.
   * @type {string}
   */
  dbPort: number

  /**
   * The username of the database. By default this is left empty.
   * @type {string}
   */
  username: string

  /**
   * The password of the database. By default this is left empty.
   * @type {string}
   */
  password: string

  /**
   * Create a new Database object.
   * @param {!PopApi} PopApi - The PopApi instance to bind the database to.
   * @param {!Object} options - The options for the database.
   * @param {!string} options.database - The arguments to be parsed by
   * @param {!Array<string>} [options.hosts=['localhost']] - The hosts for the
   * database connection.
   * @param {!number} [options.dbPort=27017] - The port for the database
   * connection.
   * @param {?string} [options.username] - The username for the database
   * connection.
   * @param {?string} [options.password] - The password for the database
   * connection.
   * @throws {TypeError} - 'database' is a required option for the Database
   * middleware!
   */
  constructor(PopApi: any, {
    database,
    hosts = ['localhost'],
    dbPort = 27017,
    username,
    password
  }: Object): void {
    const { name: debugName } = this.constructor
    PopApi.debug(`Registering ${debugName} middleware with options: %o`, {
      database,
      hosts,
      dbPort,
      username,
      password
    })

    if (!database) {
      throw new TypeError('\'database\' is a required option for the Database middleware!')
    }

    process.env.NODE_ENV = process.env.NODE_ENV || 'development'

    const {
      MONGO_PORT_27017_TCP_ADDR,
      MONGO_PORT_27017_TCP_PORT,
      NODE_ENV
    } = process.env

    this.database = `${database}-${NODE_ENV}`
    this.hosts = MONGO_PORT_27017_TCP_ADDR
      ? [MONGO_PORT_27017_TCP_ADDR]
      : hosts
    this.dbPort = Number(MONGO_PORT_27017_TCP_PORT) || dbPort
    this.username = username || ''
    this.password = password || ''

    PopApi.database = this
  }

  /**
   * Connection and configuration of the database.
   * @returns {Promise<undefined, Error>} - The promise to connect to the
   * database.
   */
  connect(): Promise<void> {
    const uri = new URL(`mongodb://${this.username}:${this.password}@${this.hosts.join(',')}:${this.dbPort}/${this.database}`)

    return mongoose.connect(uri.href)
  }

  /**
   * Disconnect from the database.
   * @returns {Promise<undefined, Error>} - The promise to disconnect from
   * the database.
   */
  disconnect(): Promise<void> {
    return mongoose.disconnect()
  }

  /**
   * Export a JSON file collection.
   * @param {!string} collection - The collection to export.
   * @param {!string} outputFile - The path of the output file of the export.
   * @returns {Promise<string, undefined>} - The promise to export a
   * collection.
   */
  exportFile(
    collection: string,
    outputFile: string
  ): Promise<string | void> {
    return executeCommand('mongoexport', [
      '-d', this.database,
      '-c', `${collection}s`,
      '-o', outputFile
    ])
  }

  /**
   * Import a JSON file collection.
   * @param {!string} collection - The collection to import.
   * @param {!string} jsonFile - The JSON file to import.
   * @returns {Promise<string, undefined>} - The promise to import a
   * collection.
   */
  importFile(
    collection: string,
    jsonFile: string
  ): Promise<string | void> {
    const file = !isAbsolute(jsonFile)
      ? jsonFile
      : join(...[__dirname, '..', '..', jsonFile])

    if (!existsSync(file)) {
      const err = new Error(`no such file found for '${file}'`)
      return Promise.reject(err)
    }

    return executeCommand('mongoimport', [
      '-d', this.database,
      '-c', `${collection}s`,
      '--file', jsonFile,
      '--upsert'
    ])
  }

}