<commit_before>import createJsonSchemaValidator from 'is-my-json-valid'
import isFunction from 'lodash.isfunction'

import { PluginsMetadata } from '../models/plugin-metadata'
import {
  InvalidParameters,
  NoSuchObject
} from '../api-errors'
import {
  createRawObject,
  noop,
  mapToArray
} from '../utils'

// ===================================================================

class NoSuchPlugin extends NoSuchObject {
  constructor (id) {
    super(id, 'plugin')
  }
}

// ===================================================================

export default class {
  constructor (xo) {
    this._plugins = createRawObject()

    this._pluginsMetadata = new PluginsMetadata({
      connection: xo._redis,
      prefix: 'xo:plugin-metadata'
    })
  }

  _getRawPlugin (id) {
    const plugin = this._plugins[id]
    if (!plugin) {
      throw new NoSuchPlugin(id)
    }
    return plugin
  }

  async _getPluginMetadata (id) {
    const metadata = await this._pluginsMetadata.first(id)
    return metadata
      ? metadata.properties
      : null
  }

  async registerPlugin (
    name,
    instance,
    configurationSchema
  ) {
    const id = name

    const plugin = this._plugins[id] = {
      configured: !configurationSchema,
      configurationSchema,
      id,
      instance,
      name,
      unloadable: isFunction(instance.unload)
    }

    const metadata = await this._getPluginMetadata(id)
    let autoload = true
    let configuration

    if (metadata) {
      ({
        autoload,
        configuration
      } = metadata)
    } else {
      console.log(`[NOTICE] register plugin ${name} for the first time`)
      await this._pluginsMetadata.save({
        id,
        autoload
      })
    }

    // Configure plugin if necessary. (i.e. configurationSchema)
    // Load plugin.
    // Ignore configuration and loading errors.
    Promise.resolve()
      .then(() => {
        if (!plugin.configured) {
          return this._configurePlugin(plugin, configuration)
        }
      })
      .then(() => {
        if (autoload) {
          return this.loadPlugin(id)
        }
      })
      .catch(noop)
  }

  async _getPlugin (id) {
    const {
      configurationSchema,
      loaded,
      name,
      unloadable
    } = this._getRawPlugin(id)
    const {
      autoload,
      configuration
    } = (await this._getPluginMetadata(id)) || {}

    return {
      id,
      name,
      autoload,
      loaded,
      unloadable,
      configuration,<commit_msg>Xo#registerPlugin(): log errors.<commit_after>import createJsonSchemaValidator from 'is-my-json-valid'
import isFunction from 'lodash.isfunction'

import { PluginsMetadata } from '../models/plugin-metadata'
import {
  InvalidParameters,
  NoSuchObject
} from '../api-errors'
import {
  createRawObject,
  mapToArray
} from '../utils'

// ===================================================================

class NoSuchPlugin extends NoSuchObject {
  constructor (id) {
    super(id, 'plugin')
  }
}

// ===================================================================

export default class {
  constructor (xo) {
    this._plugins = createRawObject()

    this._pluginsMetadata = new PluginsMetadata({
      connection: xo._redis,
      prefix: 'xo:plugin-metadata'
    })
  }

  _getRawPlugin (id) {
    const plugin = this._plugins[id]
    if (!plugin) {
      throw new NoSuchPlugin(id)
    }
    return plugin
  }

  async _getPluginMetadata (id) {
    const metadata = await this._pluginsMetadata.first(id)
    return metadata
      ? metadata.properties
      : null
  }

  async registerPlugin (
    name,
    instance,
    configurationSchema
  ) {
    const id = name

    const plugin = this._plugins[id] = {
      configured: !configurationSchema,
      configurationSchema,
      id,
      instance,
      name,
      unloadable: isFunction(instance.unload)
    }

    const metadata = await this._getPluginMetadata(id)
    let autoload = true
    let configuration

    if (metadata) {
      ({
        autoload,
        configuration
      } = metadata)
    } else {
      console.log(`[NOTICE] register plugin ${name} for the first time`)
      await this._pluginsMetadata.save({
        id,
        autoload
      })
    }

    // Configure plugin if necessary. (i.e. configurationSchema)
    // Load plugin.
    // Ignore configuration and loading errors.
    Promise.resolve()
      .then(() => {
        if (!plugin.configured) {
          return this._configurePlugin(plugin, configuration)
        }
      })
      .then(() => {
        if (autoload) {
          return this.loadPlugin(id)
        }
      })
      .catch(error => {
        console.error('register plugin %s: %s', name, error && error.stack || error)
      })
  }

  async _getPlugin (id) {
    const {
      configurationSchema,
      loaded,
      name,
      unloadable
    } = this._getRawPlugin(id)
    const {
      autoload,
      configuration
    } = (await this._getPluginMetadata(id)) || {}

    return {
      id,
      name,
      autoload,
      loaded,
      unloadable,
      configuration,
{'fraction_non_alphanumeric': 0.15473849783720015, 'fraction_numerical': 0.0, 'mean_word_length': 2.3644179894179893, 'pattern_counts': {'":': 0, '<': 3, '<?xml version=': 0, '>': 8, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '6311098', 'n_tokens_mistral': 1468, 'n_tokens_neox': 1433, 'n_words': 330}