diff --git a/src/config.js b/src/config.js index 7f57e50c0d..03072a57fb 100644 --- a/src/config.js +++ b/src/config.js @@ -2,40 +2,19 @@ const Joi = require('joi') -const schema = Joi.object({ +const ModuleSchema = Joi.alternatives().try(Joi.func(), Joi.object()) + +const OptionsSchema = Joi.object({ // TODO: create proper validators for the generics connectionManager: Joi.object(), peerInfo: Joi.object().required(), peerBook: Joi.object(), modules: Joi.object().keys({ - transport: Joi.array().items( - Joi.alternatives().try( - Joi.func(), - Joi.object() - ) - ).min(1).required(), - streamMuxer: Joi.array().items( - Joi.alternatives().try( - Joi.func(), - Joi.object() - ) - ).allow(null), - connEncryption: Joi.array().items( - Joi.alternatives().try( - Joi.func(), - Joi.object() - ) - ).allow(null), - peerDiscovery: Joi.array().items( - Joi.alternatives().try( - Joi.func(), - Joi.object() - ) - ).allow(null), - dht: Joi.alternatives().try( - Joi.func(), - Joi.object() - ).allow(null) + transport: Joi.array().items(ModuleSchema).min(1).required(), + streamMuxer: Joi.array().items(ModuleSchema).allow(null), + connEncryption: Joi.array().items(ModuleSchema).allow(null), + peerDiscovery: Joi.array().items(ModuleSchema).allow(null), + dht: ModuleSchema.allow(null) }).required(), config: Joi.object().keys({ peerDiscovery: Joi.object().allow(null), @@ -57,27 +36,12 @@ const schema = Joi.object({ }) module.exports.validate = (options) => { - let newSchema = schema - // Throw an intial error early for required props - let config = Joi.attempt(options, newSchema) - - // Ensure discoveries are properly configured - if (config.modules.peerDiscovery) { - config.modules.peerDiscovery.forEach((discovery) => { - // If it's a function, validate we have configs for it - if (typeof discovery === 'function') { - Joi.reach(schema, 'config.peerDiscovery').keys({ - [discovery.tag]: Joi.object().required() - }) - } - }) - } + options = Joi.attempt(options, OptionsSchema) // Ensure dht is correct - if (config.config.EXPERIMENTAL && config.config.EXPERIMENTAL.dht) { - newSchema = newSchema.requiredKeys('modules.dht') + if (options.config.EXPERIMENTAL.dht) { + Joi.assert(options.modules.dht, ModuleSchema.required()) } - // Finish validation and return the updated config - return Joi.attempt(config, newSchema) + return options } diff --git a/src/index.js b/src/index.js index 0a5c1f1475..b368853d8b 100644 --- a/src/index.js +++ b/src/index.js @@ -87,7 +87,7 @@ class Node extends EventEmitter { } // enable/disable pubsub - if (this._config.EXPERIMENTAL && this._config.EXPERIMENTAL.pubsub) { + if (this._config.EXPERIMENTAL.pubsub) { this.pubsub = pubsub(this) } @@ -154,15 +154,25 @@ class Node extends EventEmitter { } // all transports need to be setup before discover starts - if (this._modules.peerDiscovery && this._config.peerDiscovery) { + if (this._modules.peerDiscovery) { each(this._modules.peerDiscovery, (D, _cb) => { + let config = {} + + if (D.tag && + this._config.peerDiscovery && + this._config.peerDiscovery[D.tag]) { + config = this._config.peerDiscovery[D.tag] + } + + // If not configured to be enabled/disabled then enable by default + const enabled = config.enabled == null ? true : config.enabled + // If enabled then start it - if (this._config.peerDiscovery[D.tag].enabled) { + if (enabled) { let d if (typeof D === 'function') { - this._config.peerDiscovery[D.tag].peerInfo = this.peerInfo - d = new D(this._config.peerDiscovery[D.tag]) + d = new D(Object.assign({}, config, { peerInfo: this.peerInfo })) } else { d = D } diff --git a/test/config.spec.js b/test/config.spec.js index 9f64afd7fe..787319d844 100644 --- a/test/config.spec.js +++ b/test/config.spec.js @@ -113,21 +113,4 @@ describe('configuration', () => { expect(() => validateConfig(options)).to.throw() }) - - it('should require a non instanced peerDiscovery module to have associated options', () => { - const options = { - peerInfo, - modules: { - transport: [ WS ], - peerDiscover: [ Bootstrap ] - }, - config: { - EXPERIMENTAL: { - dht: true - } - } - } - - expect(() => validateConfig(options)).to.throw() - }) }) diff --git a/test/peer-discovery.node.js b/test/peer-discovery.node.js index 3b47b559ea..891e6dfec0 100644 --- a/test/peer-discovery.node.js +++ b/test/peer-discovery.node.js @@ -4,6 +4,7 @@ const chai = require('chai') chai.use(require('dirty-chai')) const expect = chai.expect +const sinon = require('sinon') const signalling = require('libp2p-webrtc-star/src/sig-server') const parallel = require('async/parallel') const crypto = require('crypto') @@ -58,6 +59,185 @@ describe('peer discovery', () => { }) } + describe('module registration', () => { + it('should enable by default a module passed as an object', (done) => { + const mockDiscovery = { + on: sinon.stub(), + start: sinon.stub().callsArg(0), + stop: sinon.stub().callsArg(0) + } + + const options = { modules: { peerDiscovery: [ mockDiscovery ] } } + + createNode(['/ip4/0.0.0.0/tcp/0'], options, (err, node) => { + expect(err).to.not.exist() + + node.start((err) => { + expect(err).to.not.exist() + expect(mockDiscovery.start.called).to.be.true() + node.stop(done) + }) + }) + }) + + it('should enable by default a module passed as a function', (done) => { + const mockDiscovery = { + on: sinon.stub(), + start: sinon.stub().callsArg(0), + stop: sinon.stub().callsArg(0) + } + + const MockDiscovery = sinon.stub().returns(mockDiscovery) + + const options = { modules: { peerDiscovery: [ MockDiscovery ] } } + + createNode(['/ip4/0.0.0.0/tcp/0'], options, (err, node) => { + expect(err).to.not.exist() + + node.start((err) => { + expect(err).to.not.exist() + expect(mockDiscovery.start.called).to.be.true() + node.stop(done) + }) + }) + }) + + it('should enable module by configutation', (done) => { + const mockDiscovery = { + on: sinon.stub(), + start: sinon.stub().callsArg(0), + stop: sinon.stub().callsArg(0), + tag: 'mockDiscovery' + } + + const enabled = sinon.stub().returns(true) + + const options = { + modules: { peerDiscovery: [ mockDiscovery ] }, + config: { + peerDiscovery: { + mockDiscovery: { + get enabled () { + return enabled() + } + } + } + } + } + + createNode(['/ip4/0.0.0.0/tcp/0'], options, (err, node) => { + expect(err).to.not.exist() + + node.start((err) => { + expect(err).to.not.exist() + expect(mockDiscovery.start.called).to.be.true() + expect(enabled.called).to.be.true() + node.stop(done) + }) + }) + }) + + it('should disable module by configutation', (done) => { + const mockDiscovery = { + on: sinon.stub(), + start: sinon.stub().callsArg(0), + stop: sinon.stub().callsArg(0), + tag: 'mockDiscovery' + } + + const disabled = sinon.stub().returns(false) + + const options = { + modules: { peerDiscovery: [ mockDiscovery ] }, + config: { + peerDiscovery: { + mockDiscovery: { + get enabled () { + return disabled() + } + } + } + } + } + + createNode(['/ip4/0.0.0.0/tcp/0'], options, (err, node) => { + expect(err).to.not.exist() + + node.start((err) => { + expect(err).to.not.exist() + expect(mockDiscovery.start.called).to.be.false() + expect(disabled.called).to.be.true() + node.stop(done) + }) + }) + }) + + it('should register module passed as function', (done) => { + const mockDiscovery = { + on: sinon.stub(), + start: sinon.stub().callsArg(0), + stop: sinon.stub().callsArg(0) + } + + const MockDiscovery = sinon.stub().returns(mockDiscovery) + MockDiscovery.tag = 'mockDiscovery' + + const options = { + modules: { peerDiscovery: [ MockDiscovery ] }, + config: { + peerDiscovery: { + mockDiscovery: { + enabled: true, + time: Date.now() + } + } + } + } + + createNode(['/ip4/0.0.0.0/tcp/0'], options, (err, node) => { + expect(err).to.not.exist() + + node.start((err) => { + expect(err).to.not.exist() + expect(mockDiscovery.start.called).to.be.true() + expect(MockDiscovery.called).to.be.true() + // Ensure configuration was passed + expect(MockDiscovery.firstCall.args[0]) + .to.deep.include(options.config.peerDiscovery.mockDiscovery) + node.stop(done) + }) + }) + }) + + it('should register module passed as object', (done) => { + const mockDiscovery = { + on: sinon.stub(), + start: sinon.stub().callsArg(0), + stop: sinon.stub().callsArg(0), + tag: 'mockDiscovery' + } + + const options = { + modules: { peerDiscovery: [ mockDiscovery ] }, + config: { + peerDiscovery: { + mockDiscovery: { enabled: true } + } + } + } + + createNode(['/ip4/0.0.0.0/tcp/0'], options, (err, node) => { + expect(err).to.not.exist() + + node.start((err) => { + expect(err).to.not.exist() + expect(mockDiscovery.start.called).to.be.true() + node.stop(done) + }) + }) + }) + }) + describe('MulticastDNS', () => { setup({ config: {