Home Reference Source Test

src/lib/core/md-seed-runner.js

import { Subject } from 'rxjs';

import {
  getObjectWithSelectedKeys,
  normalizeSeederName,
} from '../utils/helpers';
import MdSeedRunnerError from './md-seed-runner-error';

/**
 * mongoose-data-seed runner
 */
export default class MdSeedRunner {
  /**
   * MdSeedRunner operations constants
   * @type {Object}
   * @property {string} START        MdSeedRunner starts.
   * @property {string} SUCCESS      MdSeedRunner succeed.
   * @property {string} ERROR        MdSeedRunner finished with an error.
   */
  static operations = {
    START: 'START',
    SUCCESS: 'SUCCESS',
    ERROR: 'ERROR',
    MONGOOSE_CONNECT_START: 'MONGOOSE_CONNECT_START',
    MONGOOSE_CONNECT_SUCCESS: 'MONGOOSE_CONNECT_SUCCESS',
    MONGOOSE_CONNECT_ERROR: 'MONGOOSE_CONNECT_ERROR',
    MONGOOSE_DROP_START: 'MONGOOSE_DROP_START',
    MONGOOSE_DROP_SUCCESS: 'MONGOOSE_DROP_SUCCESS',
    MONGOOSE_DROP_ERROR: 'MONGOOSE_DROP_ERROR',
    ALL_SEEDERS_START: 'ALL_SEEDERS_START',
    ALL_SEEDERS_FINISH: 'ALL_SEEDERS_FINISH',
    SEEDER_START: 'SEEDER_START',
    SEEDER_SUCCESS: 'SEEDER_SUCCESS',
    SEEDER_ERROR: 'SEEDER_ERROR',
  };

  /**
   * Creates MdSeedRunner
   * @param {Function<Promise>}     connect     Connect to mongodb implementation
   * @param {Function<Promise>}     dropdb      Drop/Clear the database implementation
   * @param {Map<string, Function>} seedersList key=Seeder name | value=Seeder implementation
   */
  constructor({ connect, dropdb, seedersList }) {
    this.connect = connect;
    this.dropdb = dropdb;
    this.seedersList = seedersList;
    this._subject = new Subject();
  }

  run({ selectedSeeders = [], dropDatabase = false } = {}) {
    this._run({ selectedSeeders, dropDatabase });

    return this._subject.asObservable();
  }

  /*
    Private methods
   */

  async _run({ selectedSeeders, dropDatabase }) {
    const { START, SUCCESS, ERROR } = MdSeedRunner.operations;

    try {
      this._subject.next({
        type: START,
        payload: { selectedSeeders, dropDatabase },
      });

      await this._connectToMongodb();

      if (dropDatabase) {
        await this._dropDatabase();
      }

      await this._runSeeders(selectedSeeders);

      this._subject.next({
        type: SUCCESS,
        payload: { selectedSeeders, dropDatabase },
      });

      this._subject.complete();
    } catch (error) {
      const { type = ERROR, payload = { error } } = error;

      this._subject.error({ type, payload });
    }
  }

  async _connectToMongodb() {
    const {
      MONGOOSE_CONNECT_START,
      MONGOOSE_CONNECT_SUCCESS,
      MONGOOSE_CONNECT_ERROR,
    } = MdSeedRunner.operations;

    try {
      this._subject.next({ type: MONGOOSE_CONNECT_START });

      await this.connect();

      this._subject.next({ type: MONGOOSE_CONNECT_SUCCESS });
    } catch (error) {
      throw new MdSeedRunnerError({ type: MONGOOSE_CONNECT_ERROR, error });
    }
  }

  async _dropDatabase() {
    const {
      MONGOOSE_DROP_START,
      MONGOOSE_DROP_SUCCESS,
      MONGOOSE_DROP_ERROR,
    } = MdSeedRunner.operations;

    try {
      this._subject.next({ type: MONGOOSE_DROP_START });

      await this.dropdb();

      this._subject.next({ type: MONGOOSE_DROP_SUCCESS });
    } catch (error) {
      throw new MdSeedRunnerError({ type: MONGOOSE_DROP_ERROR, error });
    }
  }

  async _runSeeders(selectedSeeders) {
    const { ALL_SEEDERS_START, ALL_SEEDERS_FINISH } = MdSeedRunner.operations;

    const seeders = this._loadSelectedSeeders(selectedSeeders);

    this._subject.next({
      type: ALL_SEEDERS_START,
      payload: { seeders: Object.keys(seeders) },
    });

    for (const [name, Seeder] of Object.entries(seeders)) {
      await this._runSeeder({ name, Seeder });
    }

    this._subject.next({
      type: ALL_SEEDERS_FINISH,
      payload: { seeders: Object.keys(seeders) },
    });
  }

  async _runSeeder({ Seeder, name }) {
    const {
      SEEDER_START,
      SEEDER_SUCCESS,
      SEEDER_ERROR,
    } = MdSeedRunner.operations;

    try {
      this._subject.next({
        type: SEEDER_START,
        payload: { name },
      });

      const seeder = new Seeder();
      const results = await seeder.seed();

      this._subject.next({ type: SEEDER_SUCCESS, payload: { name, results } });
    } catch (error) {
      throw new MdSeedRunnerError({
        type: SEEDER_ERROR,
        payload: { name },
        error,
      });
    }
  }

  _loadSelectedSeeders(selectedSeeders) {
    if (selectedSeeders && selectedSeeders.length > 0) {
      return getObjectWithSelectedKeys(
        this.seedersList,
        selectedSeeders.map(name => normalizeSeederName(name))
      );
    }

    return this.seedersList;
  }
}