diff --git a/packages/convict/README.md b/packages/convict/README.md index a1a6c682..99bfa54e 100644 --- a/packages/convict/README.md +++ b/packages/convict/README.md @@ -78,6 +78,12 @@ var config = convict({ format: String, default: 'users' } + }, + admins: { + doc: 'Users with write access, or null to grant full access without login.', + format: Array, + nullable: true, + default: null } }); @@ -161,6 +167,7 @@ convict's goal of being more robust and collaborator friendly. * **Command-line arguments**: If the command-line argument specified by `arg` is supplied, it will overwrite the setting's default value or the value derived from `env`. * **Documentation**: The `doc` property is pretty self-explanatory. The nice part about having it in the schema rather than as a comment is that we can call `config.getSchemaString()` and have it displayed in the output. * **Sensitive values and secrets**: If `sensitive` is set to `true`, this value will be masked to `"[Sensitive]"` when `config.toString()` is called. This helps avoid disclosing secret keys when printing configuration at application start for debugging purposes. +* **Null values**: If `nullable` is set to `true`, the value counts as valid not only if it matches the specified `format`, but also when it is `null`. ### Validation @@ -180,6 +187,8 @@ You can find other format [here](https://www.npmjs.com/search?q=keywords:convict If `format` is set to one of the built-in JavaScript constructors, `Object`, `Array`, `String`, `Number`, `RegExp`, or `Boolean`, validation will use Object.prototype.toString.call to check that the setting is the proper type. +If `nullable` is set to true, `null` will be a valid value as well. + #### Custom format checking You can specify a custom format checking method on a property basis. diff --git a/packages/convict/src/main.js b/packages/convict/src/main.js index 9ee31fe9..e3a6a429 100644 --- a/packages/convict/src/main.js +++ b/packages/convict/src/main.js @@ -294,6 +294,11 @@ function normalizeSchema(name, node, props, fullName, env, argv, sensitive) { } o._format = function(x) { + // accept null if allowed before calling any format function + if (this.nullable && x === null) { + return + } + try { newFormat(x, this) } catch (e) { @@ -563,7 +568,7 @@ const convict = function convict(def, opts) { const parentKey = path.join('.') if (!(parentKey == '__proto__' || parentKey == 'constructor' || parentKey == 'prototype')) { const parent = walk(this._instance, parentKey, true) - parent[childKey] = v + parent[childKey] = v } return this }, diff --git a/packages/convict/test/cases/nullable.js b/packages/convict/test/cases/nullable.js new file mode 100644 index 00000000..6180d880 --- /dev/null +++ b/packages/convict/test/cases/nullable.js @@ -0,0 +1,14 @@ +'use strict' + +exports.conf = { + foo: { + default: null, + format: ['hello', 'world'], + nullable: true, + }, + bar: { + default: null, + format: String, + nullable: true, + } +} diff --git a/packages/convict/test/cases/nullable.out b/packages/convict/test/cases/nullable.out new file mode 100644 index 00000000..9e4336d1 --- /dev/null +++ b/packages/convict/test/cases/nullable.out @@ -0,0 +1,4 @@ +{ + "foo": null, + "bar": null +}