@travetto/config 3.0.0-rc.23 → 3.0.0-rc.25
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +63 -35
- package/package.json +4 -4
- package/src/configuration.ts +14 -1
package/README.md
CHANGED
|
@@ -6,6 +6,10 @@
|
|
|
6
6
|
**Install: @travetto/config**
|
|
7
7
|
```bash
|
|
8
8
|
npm install @travetto/config
|
|
9
|
+
|
|
10
|
+
# or
|
|
11
|
+
|
|
12
|
+
yarn add @travetto/config
|
|
9
13
|
```
|
|
10
14
|
|
|
11
15
|
The config module provides support for loading application config on startup. Configuration values support the common [YAML](https://en.wikipedia.org/wiki/YAML) constructs as defined in [YAML](https://github.com/travetto/travetto/tree/main/module/yaml#readme "Simple YAML support, provides only clean subset of yaml"). Additionally, the configuration is built upon the [Schema](https://github.com/travetto/travetto/tree/main/module/schema#readme "Data type registry for runtime validation, reflection and binding.") module, to enforce type correctness, and allow for validation of configuration as an
|
|
@@ -16,26 +20,24 @@ entrypoint into the application. Given that all [@Config](https://github.com/tr
|
|
|
16
20
|
The configuration information is comprised of:
|
|
17
21
|
|
|
18
22
|
|
|
19
|
-
* [YAML](https://en.wikipedia.org/wiki/YAML)
|
|
20
|
-
* environment variables
|
|
23
|
+
* configuration files - [YAML](https://en.wikipedia.org/wiki/YAML), [JSON](https://www.json.org), and basic properties file
|
|
21
24
|
* configuration classes
|
|
22
25
|
|
|
23
|
-
Config loading follows a defined resolution path, below is the order in increasing specificity:
|
|
26
|
+
Config loading follows a defined resolution path, below is the order in increasing specificity (`ext` can be `yaml`, `yml`, `json`, `properties`):
|
|
24
27
|
|
|
25
|
-
1. `resources/application
|
|
26
|
-
1. `resources
|
|
27
|
-
1. `resources/{env}
|
|
28
|
-
1. `process.env` - Read startup configuration from environment to allow for overriding any values. Because we are overriding a [YAML](https://en.wikipedia.org/wiki/YAML) based configuration we need to compensate for the differences in usage patterns. Generally all environment variables are passed in as `UPPER_SNAKE_CASE`. When reading from `process.env` we will map `UPPER_SNAKE_CASE` to `upper.snake.case`, and will attempt to match by case-insensitive name.
|
|
28
|
+
1. `resources/application.<ext>` - Load the default `application.<ext>` if available.
|
|
29
|
+
1. `resources/*.<ext>` - Load profile specific configurations as defined by the values in `process.env.TRV_PROFILES`
|
|
30
|
+
1. `resources/{env}.<ext>` - Load environment specific profile configurations as defined by the values of `process.env.TRV_ENV`.
|
|
29
31
|
|
|
30
|
-
By default all configuration data is inert, and will only be applied when constructing an instance of a configuration class.
|
|
32
|
+
By default all configuration data is inert, and will only be applied when constructing an instance of a configuration class.
|
|
31
33
|
|
|
32
|
-
|
|
34
|
+
### A Complete Example
|
|
33
35
|
|
|
34
36
|
A more complete example setup would look like:
|
|
35
37
|
|
|
36
38
|
**Config: resources/application.yml**
|
|
37
39
|
```yaml
|
|
38
|
-
|
|
40
|
+
---
|
|
39
41
|
database:
|
|
40
42
|
host: localhost
|
|
41
43
|
creds:
|
|
@@ -43,13 +45,16 @@ database:
|
|
|
43
45
|
password: test
|
|
44
46
|
```
|
|
45
47
|
|
|
46
|
-
**Config: resources/prod.
|
|
47
|
-
```
|
|
48
|
-
|
|
49
|
-
database:
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
48
|
+
**Config: resources/prod.json**
|
|
49
|
+
```json
|
|
50
|
+
{
|
|
51
|
+
"database": {
|
|
52
|
+
"host": "prod-host-db",
|
|
53
|
+
"creds": {
|
|
54
|
+
"user": "admin-user"
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
}
|
|
53
58
|
```
|
|
54
59
|
|
|
55
60
|
with environment variables
|
|
@@ -58,8 +63,6 @@ with environment variables
|
|
|
58
63
|
```properties
|
|
59
64
|
TRV_ENV = prod
|
|
60
65
|
TRV_PROFILES = prod
|
|
61
|
-
DATABASE_PORT = 1234
|
|
62
|
-
DATABASE_CREDS_PASSWORD = %secret%
|
|
63
66
|
```
|
|
64
67
|
|
|
65
68
|
At runtime the resolved config would be:
|
|
@@ -70,25 +73,51 @@ $ trv main doc/resolve.ts
|
|
|
70
73
|
|
|
71
74
|
Config {
|
|
72
75
|
sources: [
|
|
73
|
-
'
|
|
76
|
+
'application.1 - file://./doc/resources/application.yml',
|
|
77
|
+
'prod.1 - file://./doc/resources/prod.json',
|
|
74
78
|
'override.3 - memory://override'
|
|
75
79
|
],
|
|
76
80
|
active: {
|
|
77
|
-
DBConfig: {
|
|
78
|
-
host: 'localhost',
|
|
79
|
-
port: 2000,
|
|
80
|
-
creds: creds_7_45Ⲑsyn { user: 'test', password: 'test' }
|
|
81
|
-
}
|
|
81
|
+
DBConfig: { host: 'prod-host-db', port: 2000, creds: { user: 'admin-user' } }
|
|
82
82
|
}
|
|
83
83
|
}
|
|
84
84
|
```
|
|
85
85
|
|
|
86
|
-
|
|
87
|
-
|
|
86
|
+
### Custom Configuration Provider
|
|
87
|
+
In addition to files and environment variables, configuration sources can also be provided via the class itself. This is useful for reading remote configurations, or dealing with complex configuration normalization. The only caveat to this pattern, is that the these configuration sources cannot rely on the [Configuration](https://github.com/travetto/travetto/tree/main/module/config/src/configuration.ts#L14) service for input. This means any needed configuration will need to be accessed via specific patterns.
|
|
88
|
+
|
|
89
|
+
**Code: Custom Configuration Source**
|
|
90
|
+
```typescript
|
|
91
|
+
import { ConfigSource, ConfigValue } from '@travetto/config';
|
|
92
|
+
import { Injectable } from '@travetto/di';
|
|
93
|
+
|
|
94
|
+
@Injectable()
|
|
95
|
+
export class CustomConfigSource implements ConfigSource {
|
|
96
|
+
priority = 1000;
|
|
97
|
+
name = 'custom';
|
|
98
|
+
|
|
99
|
+
async getValues(): Promise<ConfigValue[]> {
|
|
100
|
+
return [
|
|
101
|
+
{
|
|
102
|
+
config: { user: { name: 'bob' } },
|
|
103
|
+
priority: this.priority,
|
|
104
|
+
profile: 'override',
|
|
105
|
+
source: `custom://${CustomConfigSource.name}`
|
|
106
|
+
}
|
|
107
|
+
];
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
## Startup
|
|
113
|
+
At startup, the [Configuration](https://github.com/travetto/travetto/tree/main/module/config/src/configuration.ts#L14) service will log out all the registered configuration objects. The configuration state output is useful to determine if everything is configured properly when diagnosing runtime errors. This service will find all configurations, and output a redacted version with all secrets removed. The default pattern for secrets is `/password|private|secret/i`. More values can be added in your configuration under the path `config.secrets`. These values can either be simple strings (for exact match), or `/pattern/` to create a regular expression.
|
|
88
114
|
|
|
89
115
|
## Consuming
|
|
90
116
|
The [Configuration](https://github.com/travetto/travetto/tree/main/module/config/src/configuration.ts#L14) service provides injectable access to all of the loaded configuration. For simplicity, a decorator, [@Config](https://github.com/travetto/travetto/tree/main/module/config/src/decorator.ts#L13) allows for classes to automatically be bound with config information on post construction via the [Dependency Injection](https://github.com/travetto/travetto/tree/main/module/di#readme "Dependency registration/management and injection support.") module. The decorator will install a `postConstruct` method if not already defined, that performs the binding of configuration. This is due to the fact that we cannot rewrite the constructor, and order of operation matters.
|
|
91
117
|
|
|
118
|
+
### Environment Variables
|
|
119
|
+
Additionally there are times in which you may want to also support configuration via environment variables. [EnvVar](https://github.com/travetto/travetto/tree/main/module/config/src/decorator.ts#L34) supports override configuration values when environment variables are present.
|
|
120
|
+
|
|
92
121
|
The decorator takes in a namespace, of what part of the resolved configuration you want to bind to your class. Given the following class:
|
|
93
122
|
|
|
94
123
|
**Code: Database config object**
|
|
@@ -107,7 +136,7 @@ export class DBConfig {
|
|
|
107
136
|
}
|
|
108
137
|
```
|
|
109
138
|
|
|
110
|
-
|
|
139
|
+
You can see that the `DBConfig` allows for the `port` to be overridden by the `DATABASE_PORT` environment variable.
|
|
111
140
|
|
|
112
141
|
**Terminal: Resolved database config**
|
|
113
142
|
```bash
|
|
@@ -119,7 +148,7 @@ $ trv main doc/dbconfig-run.ts
|
|
|
119
148
|
type: 'ValidationResultError',
|
|
120
149
|
at: 2029-03-14T04:00:00.618Z,
|
|
121
150
|
class: '@travetto/config:doc/dbconfig○DBConfig',
|
|
122
|
-
file: '
|
|
151
|
+
file: './doc/dbconfig.ts',
|
|
123
152
|
errors: [
|
|
124
153
|
{
|
|
125
154
|
kind: 'required',
|
|
@@ -142,15 +171,14 @@ $ DATABASE_PORT=200 trv main doc/dbconfig-run.ts
|
|
|
142
171
|
|
|
143
172
|
Config {
|
|
144
173
|
sources: [
|
|
145
|
-
'
|
|
174
|
+
'application.1 - file://./doc/resources/application.yml',
|
|
175
|
+
'prod.1 - file://./doc/resources/prod.json',
|
|
146
176
|
'override.3 - memory://override'
|
|
147
177
|
],
|
|
148
178
|
active: {
|
|
149
|
-
DBConfig: {
|
|
150
|
-
host: 'localhost',
|
|
151
|
-
port: 200,
|
|
152
|
-
creds: creds_7_45Ⲑsyn { user: 'test', password: 'test' }
|
|
153
|
-
}
|
|
179
|
+
DBConfig: { host: 'prod-host-db', port: 200, creds: { user: 'admin-user' } }
|
|
154
180
|
}
|
|
155
181
|
}
|
|
156
182
|
```
|
|
183
|
+
|
|
184
|
+
Additionally you may notice that the `password` field is missing, as it is redacted by default.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@travetto/config",
|
|
3
|
-
"version": "3.0.0-rc.
|
|
3
|
+
"version": "3.0.0-rc.25",
|
|
4
4
|
"description": "Configuration support",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"yaml",
|
|
@@ -26,9 +26,9 @@
|
|
|
26
26
|
"directory": "module/config"
|
|
27
27
|
},
|
|
28
28
|
"dependencies": {
|
|
29
|
-
"@travetto/di": "^3.0.0-rc.
|
|
30
|
-
"@travetto/schema": "^3.0.0-rc.
|
|
31
|
-
"@travetto/yaml": "^3.0.0-rc.
|
|
29
|
+
"@travetto/di": "^3.0.0-rc.24",
|
|
30
|
+
"@travetto/schema": "^3.0.0-rc.25",
|
|
31
|
+
"@travetto/yaml": "^3.0.0-rc.21"
|
|
32
32
|
},
|
|
33
33
|
"travetto": {
|
|
34
34
|
"displayName": "Configuration"
|
package/src/configuration.ts
CHANGED
|
@@ -26,6 +26,7 @@ export class Configuration {
|
|
|
26
26
|
#storage: Record<string, unknown> = {}; // Lowered, and flattened
|
|
27
27
|
#profiles: string[] = ['application', ...GlobalEnv.profiles, 'override'];
|
|
28
28
|
#sources: string[] = [];
|
|
29
|
+
#secrets: (RegExp | string)[] = [/password|private|secret/i];
|
|
29
30
|
|
|
30
31
|
/**
|
|
31
32
|
* Get a sub tree of the config, or everything if namespace is not passed
|
|
@@ -67,6 +68,18 @@ export class Configuration {
|
|
|
67
68
|
for (const { config: element } of sorted) {
|
|
68
69
|
DataUtil.deepAssign(this.#storage, BindUtil.expandPaths(element), 'coerce');
|
|
69
70
|
}
|
|
71
|
+
|
|
72
|
+
// Initialize Secrets
|
|
73
|
+
const userSpecified = (this.#get('config')?.secrets ?? []);
|
|
74
|
+
for (const el of Array.isArray(userSpecified) ? userSpecified : [userSpecified]) {
|
|
75
|
+
if (el !== undefined && el !== null && typeof el === 'string') {
|
|
76
|
+
if (el.startsWith('/')) {
|
|
77
|
+
this.#secrets.push(DataUtil.coerceType(el, RegExp, true));
|
|
78
|
+
} else {
|
|
79
|
+
this.#secrets.push(DataUtil.coerceType(el, String, true));
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
}
|
|
70
83
|
}
|
|
71
84
|
|
|
72
85
|
/**
|
|
@@ -89,7 +102,7 @@ export class Configuration {
|
|
|
89
102
|
const data = BindUtil.bindSchemaToObject<ConfigData>(
|
|
90
103
|
inst.constructor, {}, inst, { filterField: f => !f.secret, filterValue: v => v !== undefined }
|
|
91
104
|
);
|
|
92
|
-
out[el.class.name] = data;
|
|
105
|
+
out[el.class.name] = DataUtil.filterByKeys(data, this.#secrets);
|
|
93
106
|
}
|
|
94
107
|
return { sources: this.#sources, active: out };
|
|
95
108
|
}
|