renderers.js 5.13 KB
Newer Older
1 2 3 4 5
const Model = require('objection').Model
const path = require('path')
const fs = require('fs-extra')
const _ = require('lodash')
const yaml = require('js-yaml')
6
const DepGraph = require('dependency-graph').DepGraph
7 8 9 10 11 12 13 14 15
const commonHelper = require('../helpers/common')

/* global WIKI */

/**
 * Renderer model
 */
module.exports = class Renderer extends Model {
  static get tableName() { return 'renderers' }
16
  static get idColumn() { return 'key' }
17 18 19 20 21 22 23 24

  static get jsonSchema () {
    return {
      type: 'object',
      required: ['key', 'isEnabled'],

      properties: {
        key: {type: 'string'},
25
        isEnabled: {type: 'boolean'}
26 27 28 29
      }
    }
  }

30 31 32 33
  static get jsonAttributes() {
    return ['config']
  }

34 35 36 37
  static async getRenderers() {
    return WIKI.models.renderers.query()
  }

38 39 40 41 42 43 44 45 46 47 48 49 50
  static async fetchDefinitions() {
    const rendererDirs = await fs.readdir(path.join(WIKI.SERVERPATH, 'modules/rendering'))
    let diskRenderers = []
    for (let dir of rendererDirs) {
      const def = await fs.readFile(path.join(WIKI.SERVERPATH, 'modules/rendering', dir, 'definition.yml'), 'utf8')
      diskRenderers.push(yaml.safeLoad(def))
    }
    WIKI.data.renderers = diskRenderers.map(renderer => ({
      ...renderer,
      props: commonHelper.parseModuleProps(renderer.props)
    }))
  }

51 52 53 54 55 56
  static async refreshRenderersFromDisk() {
    let trx
    try {
      const dbRenderers = await WIKI.models.renderers.query()

      // -> Fetch definitions from disk
57
      await WIKI.models.renderers.fetchDefinitions()
58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101

      // -> Insert new Renderers
      let newRenderers = []
      for (let renderer of WIKI.data.renderers) {
        if (!_.some(dbRenderers, ['key', renderer.key])) {
          newRenderers.push({
            key: renderer.key,
            isEnabled: _.get(renderer, 'enabledDefault', true),
            config: _.transform(renderer.props, (result, value, key) => {
              _.set(result, key, value.default)
              return result
            }, {})
          })
        } else {
          const rendererConfig = _.get(_.find(dbRenderers, ['key', renderer.key]), 'config', {})
          await WIKI.models.renderers.query().patch({
            config: _.transform(renderer.props, (result, value, key) => {
              if (!_.has(result, key)) {
                _.set(result, key, value.default)
              }
              return result
            }, rendererConfig)
          }).where('key', renderer.key)
        }
      }
      if (newRenderers.length > 0) {
        trx = await WIKI.models.Objection.transaction.start(WIKI.models.knex)
        for (let renderer of newRenderers) {
          await WIKI.models.renderers.query(trx).insert(renderer)
        }
        await trx.commit()
        WIKI.logger.info(`Loaded ${newRenderers.length} new renderers: [ OK ]`)
      } else {
        WIKI.logger.info(`No new renderers found: [ SKIPPED ]`)
      }
    } catch (err) {
      WIKI.logger.error(`Failed to scan or load new renderers: [ FAILED ]`)
      WIKI.logger.error(err)
      if (trx) {
        trx.rollback()
      }
    }
  }

102 103 104 105 106 107 108 109 110 111 112 113 114
  static async getRenderingPipeline(contentType) {
    const renderersDb = await WIKI.models.renderers.query().where('isEnabled', true)
    if (renderersDb && renderersDb.length > 0) {
      const renderers = renderersDb.map(rdr => {
        const renderer = _.find(WIKI.data.renderers, ['key', rdr.key])
        return {
          ...renderer,
          config: rdr.config
        }
      })

      // Build tree
      const rawCores = _.filter(renderers, renderer => !_.has(renderer, 'dependsOn')).map(core => {
115
        core.children = _.filter(renderers, ['dependsOn', core.key])
116 117 118 119 120 121 122 123 124 125 126 127 128
        return core
      })

      // Build dependency graph
      const graph = new DepGraph({ circular: true })
      rawCores.map(core => { graph.addNode(core.key) })
      rawCores.map(core => {
        rawCores.map(coreTarget => {
          if (core.key !== coreTarget.key) {
            if (core.output === coreTarget.input) {
              graph.addDependency(core.key, coreTarget.key)
            }
          }
129 130
        })
      })
131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161

      // Filter unused cores
      let activeCoreKeys = _.filter(rawCores, ['input', contentType]).map(core => core.key)
      _.clone(activeCoreKeys).map(coreKey => {
        activeCoreKeys = _.union(activeCoreKeys, graph.dependenciesOf(coreKey))
      })
      const activeCores = _.filter(rawCores, core => _.includes(activeCoreKeys, core.key))

      // Rebuild dependency graph with active cores
      const graphActive = new DepGraph({ circular: true })
      activeCores.map(core => { graphActive.addNode(core.key) })
      activeCores.map(core => {
        activeCores.map(coreTarget => {
          if (core.key !== coreTarget.key) {
            if (core.output === coreTarget.input) {
              graphActive.addDependency(core.key, coreTarget.key)
            }
          }
        })
      })

      // Reorder cores in reverse dependency order
      let orderedCores = []
      _.reverse(graphActive.overallOrder()).map(coreKey => {
        orderedCores.push(_.find(rawCores, ['key', coreKey]))
      })

      return orderedCores
    } else {
      WIKI.logger.error(`Rendering pipeline is empty!`)
      return false
162 163 164
    }
  }
}