Skip to content

Commit d74e18b

Browse files
committed
feat: add config validation
fixed examples and tests as a result of the config validation test: add config tests and fix bugs fix: linting
1 parent 2326ce4 commit d74e18b

16 files changed

+359
-65
lines changed

examples/pubsub/1.js

+3
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,9 @@ class MyBundle extends libp2p {
2626
interval: 2000,
2727
enabled: true
2828
}
29+
},
30+
EXPERIMENTAL: {
31+
pubsub: true
2932
}
3033
}
3134
}

examples/transports/1.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
'use strict'
22

3-
const libp2p = require('libp2p')
3+
const libp2p = require('../../')
44
const TCP = require('libp2p-tcp')
55
const PeerInfo = require('peer-info')
66
const waterfall = require('async/waterfall')

examples/transports/2.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
'use strict'
22

3-
const libp2p = require('libp2p')
3+
const libp2p = require('../../')
44
const TCP = require('libp2p-tcp')
55
const PeerInfo = require('peer-info')
66
const waterfall = require('async/waterfall')

examples/transports/3.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
'use strict'
22

3-
const libp2p = require('libp2p')
3+
const libp2p = require('../../')
44
const TCP = require('libp2p-tcp')
55
const WebSockets = require('libp2p-websockets')
66
const PeerInfo = require('peer-info')

package.json

+7-2
Original file line numberDiff line numberDiff line change
@@ -36,13 +36,18 @@
3636
"url": "https://github.com/libp2p/js-libp2p/issues"
3737
},
3838
"homepage": "https://github.com/libp2p/js-libp2p",
39+
"browser": {
40+
"joi": "joi-browser"
41+
},
3942
"dependencies": {
4043
"async": "^2.6.0",
44+
"joi": "^13.4.0",
45+
"joi-browser": "^13.4.0",
4146
"libp2p-connection-manager": "~0.0.2",
42-
"libp2p-floodsub": "^0.15.0",
47+
"libp2p-floodsub": "~0.15.0",
4348
"libp2p-ping": "~0.8.0",
4449
"libp2p-switch": "~0.40.4",
45-
"libp2p-websockets": "^0.12.0",
50+
"libp2p-websockets": "~0.12.0",
4651
"mafmt": "^6.0.0",
4752
"multiaddr": "^5.0.0",
4853
"peer-book": "~0.8.0",

src/config.js

+83
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
'use strict'
2+
3+
const Joi = require('joi')
4+
5+
const schema = Joi.object({
6+
// TODO: create proper validators for the generics
7+
connectionManager: Joi.object(),
8+
peerInfo: Joi.object().required(),
9+
peerBook: Joi.object(),
10+
modules: Joi.object().keys({
11+
transport: Joi.array().items(
12+
Joi.alternatives().try(
13+
Joi.func(),
14+
Joi.object()
15+
)
16+
).min(1).required(),
17+
streamMuxer: Joi.array().items(
18+
Joi.alternatives().try(
19+
Joi.func(),
20+
Joi.object()
21+
)
22+
).allow(null),
23+
connEncryption: Joi.array().items(
24+
Joi.alternatives().try(
25+
Joi.func(),
26+
Joi.object()
27+
)
28+
).allow(null),
29+
peerDiscovery: Joi.array().items(
30+
Joi.alternatives().try(
31+
Joi.func(),
32+
Joi.object()
33+
)
34+
).allow(null),
35+
dht: Joi.alternatives().try(
36+
Joi.func(),
37+
Joi.object()
38+
).allow(null)
39+
}).required(),
40+
config: Joi.object().keys({
41+
peerDiscovery: Joi.object().allow(null),
42+
relay: Joi.object().keys({
43+
enabled: Joi.boolean().default(false),
44+
hop: Joi.object().keys({
45+
enabled: Joi.boolean().default(false),
46+
active: Joi.boolean().default(false)
47+
})
48+
}).default(),
49+
dht: Joi.object().keys({
50+
kBucketSize: Joi.number().allow(null)
51+
}),
52+
EXPERIMENTAL: Joi.object().keys({
53+
dht: Joi.boolean().default(false),
54+
pubsub: Joi.boolean().default(false)
55+
}).default()
56+
}).default()
57+
})
58+
59+
module.exports.validate = (options) => {
60+
let newSchema = schema
61+
// Throw an intial error early for required props
62+
let config = Joi.attempt(options, newSchema)
63+
64+
// Ensure discoveries are properly configured
65+
if (config.modules.peerDiscovery) {
66+
config.modules.peerDiscovery.forEach((discovery) => {
67+
// If it's a function, validate we have configs for it
68+
if (typeof discovery === 'function') {
69+
Joi.reach(schema, 'config.peerDiscovery').keys({
70+
[discovery.tag]: Joi.object().required()
71+
})
72+
}
73+
})
74+
}
75+
76+
// Ensure dht is correct
77+
if (config.config.EXPERIMENTAL && config.config.EXPERIMENTAL.dht) {
78+
newSchema = newSchema.requiredKeys('modules.dht')
79+
}
80+
81+
// Finish validation and return the updated config
82+
return Joi.attempt(config, newSchema)
83+
}

src/index.js

+27-22
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,9 @@
33
const EventEmitter = require('events').EventEmitter
44
const assert = require('assert')
55

6-
const setImmediate = require('async/setImmediate')
76
const each = require('async/each')
87
const series = require('async/series')
8+
const parallel = require('async/parallel')
99

1010
const PeerBook = require('peer-book')
1111
const Switch = require('libp2p-switch')
@@ -18,6 +18,7 @@ const contentRouting = require('./content-routing')
1818
const dht = require('./dht')
1919
const pubsub = require('./pubsub')
2020
const getPeerInfo = require('./get-peer-info')
21+
const validateConfig = require('./config').validate
2122

2223
exports = module.exports
2324

@@ -26,15 +27,15 @@ const NOT_STARTED_ERROR_MESSAGE = 'The libp2p node is not started yet'
2627
class Node extends EventEmitter {
2728
constructor (_options) {
2829
super()
29-
assert(_options.modules, 'requires modules to equip libp2p with features')
30-
assert(_options.peerInfo, 'requires a PeerInfo instance')
30+
// validateConfig will ensure the config is correct,
31+
// and add default values where appropriate
32+
_options = validateConfig(_options)
3133

3234
this.peerInfo = _options.peerInfo
3335
this.peerBook = _options.peerBook || new PeerBook()
3436

3537
this._modules = _options.modules
36-
// TODO populate with default config, if any
37-
this._config = _options.config || {}
38+
this._config = _options.config
3839
this._isStarted = false
3940
this._transport = [] // Transport instances/references
4041
this._discovery = [] // Discovery service instances/references
@@ -46,7 +47,6 @@ class Node extends EventEmitter {
4647
// Attach stream multiplexers
4748
if (this._modules.streamMuxer) {
4849
let muxers = this._modules.streamMuxer
49-
muxers = Array.isArray(muxers) ? muxers : [muxers]
5050
muxers.forEach((muxer) => this._switch.connection.addStreamMuxer(muxer))
5151

5252
// If muxer exists
@@ -70,16 +70,13 @@ class Node extends EventEmitter {
7070
// Attach crypto channels
7171
if (this._modules.connEncryption) {
7272
let cryptos = this._modules.connEncryption
73-
cryptos = Array.isArray(cryptos) ? cryptos : [cryptos]
7473
cryptos.forEach((crypto) => {
7574
this._switch.connection.crypto(crypto.tag, crypto.encrypt)
7675
})
7776
}
7877

7978
// dht provided components (peerRouting, contentRouting, dht)
80-
if (this._config.EXPERIMENTAL &&
81-
this._config.EXPERIMENTAL.dht &&
82-
this._modules.dht) {
79+
if (this._config.EXPERIMENTAL.dht) {
8380
const DHT = this._modules.dht
8481
this._dht = new DHT(this._switch, {
8582
kBucketSize: this._config.dht.kBucketSize || 20,
@@ -91,14 +88,13 @@ class Node extends EventEmitter {
9188

9289
// enable/disable pubsub
9390
if (this._config.EXPERIMENTAL && this._config.EXPERIMENTAL.pubsub) {
94-
// TODO only enable PubSub if flag is set to true
91+
this.pubsub = pubsub(this)
9592
}
9693

9794
// Attach remaining APIs
9895
this.peerRouting = peerRouting(this)
9996
this.contentRouting = contentRouting(this)
10097
this.dht = dht(this)
101-
this.pubsub = pubsub(this)
10298

10399
this._getPeerInfo = getPeerInfo(this)
104100

@@ -195,7 +191,7 @@ class Node extends EventEmitter {
195191
(cb) => {
196192
// TODO: chicken-and-egg problem #2:
197193
// have to set started here because FloodSub requires libp2p is already started
198-
if (this._options !== false) {
194+
if (this._floodSub) {
199195
this._floodSub.start(cb)
200196
} else {
201197
cb()
@@ -225,17 +221,24 @@ class Node extends EventEmitter {
225221
* Stop the libp2p node by closing its listeners and open connections
226222
*/
227223
stop (callback) {
228-
if (this._modules.peerDiscovery) {
229-
this._discovery.forEach((d) => {
230-
setImmediate(() => d.stop(() => {}))
231-
})
232-
}
233-
234224
series([
235225
(cb) => {
236-
if (this._floodSub.started) {
237-
this._floodSub.stop(cb)
226+
if (this._modules.peerDiscovery) {
227+
// stop all discoveries before continuing with shutdown
228+
return parallel(
229+
this._discovery.map((d) => {
230+
return (_cb) => d.stop(() => { _cb() })
231+
}),
232+
cb
233+
)
234+
}
235+
cb()
236+
},
237+
(cb) => {
238+
if (this._floodSub) {
239+
return this._floodSub.stop(cb)
238240
}
241+
cb()
239242
},
240243
(cb) => {
241244
if (this._dht) {
@@ -303,7 +306,9 @@ class Node extends EventEmitter {
303306
}
304307

305308
ping (peer, callback) {
306-
assert(this.isStarted(), NOT_STARTED_ERROR_MESSAGE)
309+
if (!this.isStarted()) {
310+
return callback(new Error(NOT_STARTED_ERROR_MESSAGE))
311+
}
307312

308313
this._getPeerInfo(peer, (err, peerInfo) => {
309314
if (err) { return callback(err) }

0 commit comments

Comments
 (0)