/**
* This file is part of "Lassi".
* Copyright 2009-2012, arNuméral
* Author : Yoran Brault
* eMail : yoran.brault@arnumeral.fr
* Site : http://arnumeral.fr
*
* "Lassi" is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation; either version 2.1 of
* the License, or (at your option) any later version.
*
* "Lassi" is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public
* License along with "Lassi"; if not, write to the Free
* Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
* 02110-1301 USA, or see the FSF site: http://www.fsf.org.
*/
'use strict'
const _ = require('lodash')
const flow = require('an-flow')
const EventEmitter = require('events').EventEmitter
const MongoClient = require('mongodb').MongoClient
// const { hasProp } = require('sesajstools')
const EntityDefinition = require('./EntityDefinition')
// on reprend le même log que EntityDefinition
const log = require('an-log')('EntityDefinition')
const defaultPoolSize = 10
class Entities extends EventEmitter {
/**
* Construction du gestionnaire d'entités.
* À ne jamais utiliser directement. Cette classe est instanciée par
* l'application elle-même.
* @constructor
* @namespace
* @param {Object} settings
*/
constructor (settings) {
super()
this.client = null
this.db = null
this.entities = {}
this.settings = settings
}
/**
* Ferme la connexion ouverte dans initialize
* (reset this.db mais pas this.entities)
*/
close (next) {
if (this.client) {
this.client.close(false, (error) => {
if (error) console.error(error)
if (next) next(error)
})
// on met ça à null tout de suite, pas très grave si qqun relançait un client alors que close n'avait pas terminé
this.client = null
this.db = null
}
}
/**
* Enregistre une entité dans le modèle.
* Cette méthode est appelée automatiquement par l'application lors de
* l'exploration des composants.
* @param {Entity} entity le module
*/
define (name) {
const def = new EntityDefinition(name)
def._bless(this)
this.entities[name] = def
return def
}
/**
* Retourne la liste des EntityDefinitions existantes
* @return {object}
*/
definitions () {
return this.entities
}
/**
* Initialisation de l'espace de stockage puis appel de chaque _initialize des entity déjà définies
* @param {simpleCallback} cb
*/
initialize (cb) {
const self = this
if (self.client) return cb(Error('entities.initialize a déjà été appelé'))
const settings = self.settings.database
const { name, host, port, user, password, authSource } = settings
// cf http://mongodb.github.io/node-mongodb-native/3.5/reference/connecting/connection-settings/
// et http://mongodb.github.io/node-mongodb-native/3.5/api/MongoClient.html pour les options possibles
const options = settings.options || {}
// pour compatibilité ascendante, poolSize était mis directement dans les settings
if (settings.poolSize && !options.poolSize) {
console.error(`poolsize doit désormais être indiqué dans database.options.poolsize (defaut : ${defaultPoolSize})`)
options.poolSize = settings.poolSize
}
if (!options.poolSize) options.poolSize = defaultPoolSize
if (authSource) options.authSource = authSource
// à partir de la version 3.1 il faut passer ça pour éviter un warning
// if (!hasProp(options, 'useNewUrlParser')) options.useNewUrlParser = true
options.useUnifiedTopology = true
// construction de l'url de connexion, cf docs.mongodb.org/manual/reference/connection-string/
let url = 'mongodb://'
if (user && password) url += `${encodeURIComponent(user)}:${encodeURIComponent(password)}@`
url += `${host}:${port}/${name}`
flow().seq(function () {
MongoClient.connect(url, options, this)
}).seq(function (client) {
client.on('connectionCreated', event => log('événement mongo connectionCreated', Object.entries(event).map(([k, v]) => `${k} : ${v}`).join(', ')))
self.client = client
self.db = client.db()
// on passe à l'init de toutes les entities
this(null, Object.values(self.entities))
}).seqEach(function (entityDefinition) {
entityDefinition._initialize(this)
}).empty().done(cb)
}
/**
* Reconstruction des indexes d'une entité.
* @param {Entity} entity L'entité dont on supprime l'indexe.
* @param {simpleCallback} next callback de retour
* @private
*/
rebuildEntityIndexes (entity, next) {
flow().seq(function () {
entity.match().grab(this)
}).seqEach(function (object) {
object.store(this)
}).done(next)
}
/**
* Reconstruction des indexes.
* Cette méthode est appelée par les commandes `lassi entities-XXX`
* @param {simpleCallback} next callback de retour.
*/
rebuildIndexes (next) {
const self = this
flow(_.values(this.entities)).seqEach(function (entity) {
self.rebuildEntityIndexes(entity, this)
}).done(next)
}
}
module.exports = Entities