antarestupin/Avifors

View on GitHub
src/model/YamlModelBuilder.js

Summary

Maintainability
A
2 hrs
Test Coverage
import fs from 'fs'
import glob from 'glob'
import yaml from 'js-yaml'
import chalk from 'chalk'

export default class YamlModelBuilder {
  constructor(avifors, yamlHelper) {
    this.avifors = avifors
    this.yamlHelper = yamlHelper
  }

  build(paths) {
    return paths
      .map(path => glob.sync(path, { nodir: true })) // get the list of files matching given pattern
      .reduce((a,b) => a.concat(b)) // flatten it to one list
      .map(path => this.yamlHelper.readYamlFile(path))
      .map(modelConfig => this._normalizeModelConfig(modelConfig))
      .reduce((a,b) => a.concat(b))
  }

  /**
   * entities:
   *   User:
   *     properties: ...
   *
   * => [
   *   {
   *     type: 'entity',
   *     arguments: {
   *       name: 'User',
   *       properties: ...
   *     }
   *   }
   * ]
   */
  _normalizeModelConfig(modelConfig) {
    let name = Object.keys(modelConfig)[0]
    let [generator, isList] = this.avifors.getGenerator(name)

    if (isList) {
      // if the key is used as an argument
      if (!Array.isArray(modelConfig[name])) {
        let argsList = []
        for (let i in modelConfig[name]) {
          argsList.push({
            [generator.key]: i,
            ...modelConfig[name][i]
          })
        }
        modelConfig[name] = argsList
      }

      modelConfig[name] = modelConfig[name].map(args => this._executeConstructors(args))
      modelConfig[name].forEach(args => this._validateItem(args, generator))

      return modelConfig[name].map(args => ({
        type: generator.name,
        arguments: generator.arguments.build(args)
      }))
    }

    modelConfig[name] = this._executeConstructors(modelConfig[name])
    this._validateItem(modelConfig[name], generator)

    return [{
      type: name,
      arguments: generator.arguments.build(modelConfig[name])
    }]
  }

  _executeConstructors(item) {
    if (typeof item === 'string') {
      const constructor = item.match(/^\s*(\.\w+)+(\(.*\))?\s*$/)
      if (constructor !== null) {
        try {
          return eval("(this.avifors.constructors" + item + ")")
        } catch (exception) {
          throw `${chalk.bold.red('Error during constructor exectution')} - ${item}.\n\n${exception.message}`
        }
      }
    }

    if (Array.isArray(item)) {
      return item.map(i => this._executeConstructors(i))
    }

    if (typeof item === 'object' && item !== null) {
      let res = {}
      for (let i in item) {
        res[i] = this._executeConstructors(item[i])
      }
      return res
    }

    return item
  }

  _validateItem(args, generator) {
    try {
      generator.arguments.validate(args, '')
    } catch(e) {
      throw `${chalk.bold.red('Error during model item validation:')} ${e}\n\n`
        + `Item generating this error:\n\n`
        + this.yamlHelper.print(args)
    }
  }
}