Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add store.bind(), remove $env directive, misc fixes #110

Closed
wants to merge 5 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
233 changes: 60 additions & 173 deletions API.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,11 +12,11 @@ Dynamic, declarative configurations
- [`store.load(document)`](#storeloaddocument)
- [`store.get(key, [criteria])`](#storegetkey-criteria)
- [`store.meta(key, [criteria])`](#storemetakey-criteria)
- [`store.bind(criteria)`](#storebindcriteria)
- [Document Format](#document-format)
- [Basic Structure](#basic-structure)
- [Environment Variables](#environment-variables)
- [Coercing value](#coercing-value)
- [Criteria Parameters](#criteria-parameters)
- [Coercing value](#coercing-value)
- [Filters](#filters)
- [Ranges](#ranges)
- [Metadata](#metadata)
Expand Down Expand Up @@ -89,6 +89,15 @@ Returns the metadata found after applying the criteria. If the key is invalid or
const value = store.meta('/c', { size: 'big' });
```

#### `store.bind([criteria])`

Binds criteria directly to the store, effectively setting it as default criteria. When `criteria` is passed to [`store.get()`](#storegetkey-criteria) or [`store.meta()`](#storemetakey-criteria), it is merged into the bound criteria. When `store.bind()` is called multiple times, the criteria from each call are merged together. Calling `store.bind()` without an argument will reset the bound criteria.

```javascript
store.bind({ size: 'big' });
const value = store.get('/c');
```

## Document Format

Confidence builds on top of a plain object as its document.
Expand Down Expand Up @@ -117,69 +126,38 @@ Keys can have children:
}
```

### Environment Variables

In many scenarios, configuration documents may need to pull values from environment variables. Confidence allows you to refer to environment variables using `$env` directive.

```json
{
"mysql": {
"host": { "$env" : "MYSQL_HOST" },
"port": { "$env" : "MYSQL_PORT" },
"user": { "$env" : "MYSQL_USER" },
"password": { "$env" : "MYSQL_PASSWORD" },
"database": { "$env" : "MYSQL_DATABASE" },
}
}
```

With following Enviornment Variables:

```sh
MYSQL_HOST=xxx.xxx.xxx.xxx
MYSQL_PORT=3306
MYSQL_USER=user1
MYSQL_PASSWORD=some_password
MYSQL_DATABASE=live_db
```
### Criteria Parameters

The result is:
In many scenarios, configuration documents may need to pull values fron `criteria`. Confidence allows you to refer to `criteria` using `$param` directive.

```json
{
"mysql": {
"host": "xxx.xxx.xxx.xxx",
"port": "3306",
"user": "user1",
"password": "some_password",
"database": "live_db"
"host": { "$param" : "credentials.mysql.host" },
"port": { "$param" : "credentials.mysql.port" },
"user": { "$param" : "credentials.mysql.user" },
"password": { "$param" : "credentials.mysql.password" },
"database": { "$param" : "credentials.mysql.database" },
}
}
```

`$default` directive allows to fallback to default values in case an environment variable is not set.
With following `criteria`:

```json
{
"mysql": {
"host": { "$env" : "MYSQL_HOST" },
"port": { "$env" : "MYSQL_PORT", "$default": 3306 },
"user": { "$env" : "MYSQL_USER" },
"password": { "$env" : "MYSQL_PASSWORD" },
"database": { "$env" : "MYSQL_DATABASE" },
"crendentials": {
"mysql": {
"host": "xxx.xxx.xxx.xxx",
"port": 3306,
"user": "user1",
"password": "some_password",
"database": "live_db"
}
}
}
```

With following Enviornment Variables:

```sh
MYSQL_HOST=xxx.xxx.xxx.xxx
MYSQL_USER=user1
MYSQL_PASSWORD=some_password
MYSQL_DATABASE=live_db
```

The result is:

```json
Expand All @@ -194,58 +172,36 @@ The result is:
}
```

#### Coercing value

`$coerce` directive allows you to coerce values to different types. In case the coercing fails, it falls back to `$default` directive, if present. Otherwise it return `undefined`.
`$default` directive allows to fallback to default values in case a criteria is `undefined` or `null`.

```json
{
"mysql": {
"host": { "$env" : "MYSQL_HOST" },
"port": {
"$env" : "MYSQL_PORT",
"$coerce": "number",
"$default": 3306
},
"user": { "$env" : "MYSQL_USER" },
"password": { "$env" : "MYSQL_PASSWORD" },
"database": { "$env" : "MYSQL_DATABASE" },
"host": { "$param" : "credentials.mysql.host" },
"port": { "$param" : "credentials.mysql.port", "$default": 3306 },
"user": { "$param" : "credentials.mysql.user" },
"password": { "$param" : "credentials.mysql.password" },
"database": { "$param" : "credentials.mysql.database" },
}
}
```

With following Environment Variables:

```sh
MYSQL_HOST=xxx.xxx.xxx.xxx
MYSQL_PORT=3316
MYSQL_USER=user1
MYSQL_PASSWORD=some_password
MYSQL_DATABASE=live_db
```

The result is:
With following `criteria`:

```json
{
"mysql": {
"host": "xxx.xxx.xxx.xxx",
"port": 3316,
"user": "user1",
"password": "some_password",
"database": "live_db"
"credentials": {
"mysql": {
"host": "xxx.xxx.xxx.xxx",
"port": null,
"user": "user1",
"password": "some_password",
"database": "live_db"
}
}
}
```
With following Environment Variables:

```sh
MYSQL_HOST=xxx.xxx.xxx.xxx
MYSQL_PORT=unknown
MYSQL_USER=user1
MYSQL_PASSWORD=some_password
MYSQL_DATABASE=live_db
```

The result is:

Expand All @@ -261,24 +217,16 @@ The result is:
}
```

Value can be coerced to :
- `number` : applying `Number(value)`
- `boolean` : checking whether the value equal `true` or `false` case insensitive
- `array` : applying a `value.split(token)` with `token` (by default `','`) modifiable by setting the key `$splitToken` to either a string or a regex
- `object` : applying a `JSON.parse(value)`

### Criteria Parameters
#### Coercing value

In many scenarios, configuration documents may need to pull values fron `criteria`. Confidence allows you to refer to `criteria` using `$param` directive.
`$coerce` directive allows you to coerce values to different types. In case the coercing fails, it falls back to `$default` directive, if present. Otherwise it returns `undefined`.

```json
{
"mysql": {
"host": { "$param" : "credentials.mysql.host" },
"port": { "$param" : "credentials.mysql.port" },
"user": { "$param" : "credentials.mysql.user" },
"password": { "$param" : "credentials.mysql.password" },
"database": { "$param" : "credentials.mysql.database" },
"port": {
"$param" : "service.port",
"$coerce": "number",
"$default": 3000
}
}
```
Expand All @@ -287,14 +235,8 @@ With following `criteria`:

```json
{
"crendentials": {
"mysql": {
"host": "xxx.xxx.xxx.xxx",
"port": 3306,
"user": "user1",
"password": "some_password",
"database": "live_db"
}
"service": {
"port": "4000"
}
}
```
Expand All @@ -303,43 +245,16 @@ The result is:

```json
{
"mysql": {
"host": "xxx.xxx.xxx.xxx",
"port": "3306",
"user": "user1",
"password": "some_password",
"database": "live_db"
}
"port": 4000
}
```

`$default` directive allows to fallback to default values in case a criteria is `undefined` or `null`.

```json
{
"mysql": {
"host": { "$param" : "credentials.mysql.host" },
"port": { "$param" : "credentials.mysql.port", "$default": 3306 },
"user": { "$param" : "credentials.mysql.user" },
"password": { "$param" : "credentials.mysql.password" },
"database": { "$param" : "credentials.mysql.database" },
}
}

```

With following `criteria`:

```json
{
"credentials": {
"mysql": {
"host": "xxx.xxx.xxx.xxx",
"port": null,
"user": "user1",
"password": "some_password",
"database": "live_db"
}
"service": {
"port": "unknown"
}
}
```
Expand All @@ -348,16 +263,16 @@ The result is:

```json
{
"mysql": {
"host": "xxx.xxx.xxx.xxx",
"port": 3306,
"user": "user1",
"password": "some_password",
"database": "live_db"
}
"port": 3000
}
```

Value can be coerced to:
- `number` : applying `Number(value)`.
- `boolean` : checking whether the value equal `true` or `false` case insensitive.
- `array` : applying a `value.split(token)` with `token` (by default `','`) modifiable by setting the key `$splitToken` to either a string or a regex. Empty strings are coerced to `[]`.
- `object` : applying JSON parsing to the string.


### Filters

Expand Down Expand Up @@ -401,34 +316,6 @@ Filters can have a default value which will be used if the provided criteria set
}
}
```
Filters can also refer to environment variables using `$env` directive.

```json
{
"key1": "abc",
"key2": {
"$filter": { "$env": "NODE_ENV" },
"production": {
"host": { "$env" : "MYSQL_HOST" },
"port": {
"$env" : "MYSQL_PORT",
"$coerce": "number",
"$default": 3306
},
"user": { "$env" : "MYSQL_USER" },
"password": { "$env" : "MYSQL_PASSWORD" },
"database": { "$env" : "MYSQL_DATABASE" },
},
"$default": {
"host": "127.0.0.1",
"port": 3306,
"user": "dev",
"password": "password",
"database": "dev_db"
}
}
}
```

### Ranges

Expand Down
18 changes: 5 additions & 13 deletions lib/schema.js
Original file line number Diff line number Diff line change
Expand Up @@ -152,18 +152,12 @@ exports.store = internals.store = internals.Joi.object().keys({
$param: internals.Joi.string().regex(/^\w+(?:\.\w+)*$/, { name: 'Alphanumeric Characters and "_"' }),
$value: internals.alternatives,
$replace: internals.Joi.boolean().invalid(false),
$env: internals.Joi.string().regex(/^\w+$/, { name: 'Alphanumeric Characters and "_"' }),
$coerce: internals.Joi.string().valid('number', 'array', 'boolean', 'object'),
$splitToken: internals.Joi.alternatives([
internals.Joi.string(),
internals.Joi.object().instance(RegExp)
]),
$filter: internals.Joi.alternatives([
internals.Joi.string().regex(/^\w+(?:\.\w+)*$/, { name: 'Alphanumeric Characters and "_"' }),
internals.Joi.object().keys({
$env: internals.Joi.string().regex(/^\w+$/, { name: 'Alphanumeric Characters and "_"' }).required()
})
]),
$filter: internals.Joi.string().regex(/^\w+(?:\.\w+)*$/, { name: 'Alphanumeric Characters and "_"' }),
$base: internals.alternatives,
$default: internals.alternatives,
$id: internals.Joi.string(),
Expand All @@ -180,14 +174,12 @@ exports.store = internals.store = internals.Joi.object().keys({
.notInstanceOf(Error)
.notInstanceOf(RegExp)
.notInstanceOf(Date)
.without('$value', ['$filter', '$range', '$base', '$default', '$id', '$param', '$env'])
.without('$param', ['$filter', '$range', '$base', '$id', '$value', '$env'])
.without('$env', ['$filter', '$range', '$base', '$id', '$value', '$param'])
.without('$value', ['$filter', '$range', '$base', '$default', '$id', '$param'])
.without('$param', ['$filter', '$range', '$base', '$id', '$value'])
.withPattern('$value', /^([^\$].*)$/, { name: '$value directive can only be used with $meta or $default or nothing' })
.withPattern('$param', /^([^\$].*)$/, { name: '$param directive can only be used with $meta or $default or nothing' })
.withPattern('$env', /^([^\$].*)$/, { name: '$env directive can only be used with $meta or $default or nothing' })
.withPattern('$default', /^((\$param)|(\$filter)|(\$env))$/, { inverse: true, name: '$default directive requires $filter or $param or $env' })
.withPattern('$coerce', /^((\$param)|(\$env))$/, { inverse: true, name: '$coerce directive requires $param or $env' })
.withPattern('$default', /^((\$param)|(\$filter))$/, { inverse: true, name: '$default directive requires $filter or $param' })
.withPattern('$coerce', /^((\$param))$/, { inverse: true, name: '$coerce directive requires $param' })
.with('$range', '$filter')
.with('$base', '$filter')
.with('$splitToken', '$coerce')
Expand Down
Loading