@toa.io/extensions.configuration 1.1.0-dev.11 → 1.1.0-dev.13

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@toa.io/extensions.configuration",
3
- "version": "1.1.0-dev.11",
3
+ "version": "1.1.0-dev.13",
4
4
  "description": "Toa Configuration",
5
5
  "author": "temich <tema.gurtovoy@gmail.com>",
6
6
  "homepage": "https://github.com/toa-io/toa#readme",
@@ -16,11 +16,11 @@
16
16
  "access": "public"
17
17
  },
18
18
  "dependencies": {
19
- "@toa.io/core": "1.1.0-dev.11",
20
- "@toa.io/generic": "0.11.0-dev.11",
21
- "@toa.io/schema": "0.7.6-dev.11",
22
- "@toa.io/yaml": "0.7.6-dev.11",
19
+ "@toa.io/core": "1.1.0-dev.13",
20
+ "@toa.io/generic": "0.11.0-dev.13",
21
+ "@toa.io/schema": "0.7.6-dev.13",
22
+ "@toa.io/yaml": "0.7.6-dev.13",
23
23
  "clone-deep": "4.0.1"
24
24
  },
25
- "gitHead": "43f790e208d5eea38a8d3f09136dc1221d26ccc3"
25
+ "gitHead": "8c824ed7448e01f0ddf44500a4a6e692d35eaa1b"
26
26
  }
package/readme.md CHANGED
@@ -163,11 +163,6 @@ configuration:
163
163
  bar@staging: 2
164
164
  ```
165
165
 
166
- ### Local environment
167
-
168
- Configuration Objects for local environment may be created
169
- by [`toa configure`](../../runtime/cli/readme.md#configure) command.
170
-
171
166
  ## Configuration Secrets
172
167
 
173
168
  Context Configuration values which are uppercase strings prefixed with `$` considered as Secrets.
@@ -196,13 +191,13 @@ Deployed kubernetes secret's name is predefined as `configuration`.
196
191
  $ toa conceal configuration STRIPE_API_KEY xxxxxxxx
197
192
  ```
198
193
 
199
- ## Operation Context
200
-
201
- Configuration Value is available as a well-known operation context extension `configuration`.
194
+ ## Aspect
202
195
 
203
- ### Usage: node
196
+ Configuration Value is available as a well-known operation Aspect `configuration`.
204
197
 
205
198
  ```javascript
199
+ // Node.js bridge
200
+
206
201
  function transition (input, entity, context) {
207
202
  const foo = context.configiuration.foo
208
203
 
@@ -216,21 +211,46 @@ function transition (input, entity, context) {
216
211
  > from [hot updates](#).
217
212
  >
218
213
  > ```javascript
219
- > // THIS IS WEIRD, BAD AND NOT RECOMMENDED
214
+ > // NOT RECOMMENDED
220
215
  > let foo
221
216
  >
222
217
  > function transition (input, entity, context) {
218
+ > // NOT RECOMMENDED
223
219
  > if (foo === undefined) foo = context.configuration.foo
224
220
  >
225
221
  > // ...
226
222
  > }
227
223
  > ```
228
- > See [Genuine operations](#).
224
+ > See [Genuine operations](/documentation/design.md#genuine-operations).
225
+
226
+ ## Development Configuration
227
+
228
+ Configuration can be exported by [`toa env`](/runtime/cli/readme.md#env).
229
+
230
+ ### Local Environment Placeholders
231
+
232
+ Context Configuration values may contain placeholders that reference environment variables.
233
+ Placeholders are replaced with values if the corresponding environment variables are set.
234
+
235
+ > Placeholders can only be used with local environment (exported by `toa env`), as these values are not
236
+ > deployed.
237
+
238
+ ```yaml
239
+ # context.toa.yaml
240
+ configuration:
241
+ dummies.dummy:
242
+ url@local: https://stage${STAGE}.intranet/
243
+ ```
244
+
245
+ ```dotenv
246
+ # .env
247
+ STAGE=82
248
+ ```
229
249
 
230
250
  ## Appendix
231
251
 
232
252
  - [Discussion](./docs/discussion.md)
233
253
  - [Configuration consistency](./docs/consistency.md)
234
254
 
235
- [^1]: Cannot be changed without a deployment. New values are considered to be a subject of
255
+ [^1]: Cannot be changed without a deployment as new values are considered to be a subject of
236
256
  testing. [#146](https://github.com/toa-io/toa/issues/146)
@@ -0,0 +1,19 @@
1
+ 'use strict'
2
+
3
+ const { map } = require('@toa.io/generic')
4
+
5
+ function env (object) {
6
+ return map(object,
7
+ /**
8
+ * @type {toa.generic.map.transform<string>}
9
+ */
10
+ (value) => {
11
+ if (typeof value !== 'string') return
12
+
13
+ return value.replaceAll(RX, (match, variable) => process.env[variable] ?? match)
14
+ })
15
+ }
16
+
17
+ const RX = /\${(?<variable>[A-Z0-9_]{1,32})}/g
18
+
19
+ exports.env = env
@@ -0,0 +1,7 @@
1
+ 'use strict'
2
+
3
+ const { form } = require('./form')
4
+ const { env } = require('./env')
5
+
6
+ exports.form = form
7
+ exports.env = env
@@ -6,7 +6,7 @@ const { decode, encode, empty, overwrite } = require('@toa.io/generic')
6
6
  const { Connector } = require('@toa.io/core')
7
7
 
8
8
  const { secrets } = require('./secrets')
9
- const { form } = require('./.provider/form')
9
+ const { env, form } = require('./.provider')
10
10
 
11
11
  /**
12
12
  * @implements {toa.extensions.configuration.Provider}
@@ -88,6 +88,8 @@ class Provider extends Connector {
88
88
 
89
89
  #set (object) {
90
90
  object = this.#reveal(object)
91
+ object = env(object)
92
+
91
93
  this.#merge(object)
92
94
 
93
95
  this.object = empty(object) ? undefined : object
@@ -1,5 +1,7 @@
1
1
  'use strict'
2
2
 
3
+ /* eslint-disable no-template-curly-in-string */
4
+
3
5
  const { generate } = require('randomstring')
4
6
  const { encode } = require('@toa.io/generic')
5
7
 
@@ -52,6 +54,44 @@ it('should replace nested secrets', async () => {
52
54
  expect(value).toStrictEqual({ foo: { bar: secrets.BAR } })
53
55
  })
54
56
 
57
+ it('should replace placeholders', async () => {
58
+ const name = 'FOO_VALUE'
59
+ const configuration = { foo: { bar: 'foo_${' + name + '}' } }
60
+ const value = generate()
61
+
62
+ setEnv(configuration)
63
+ setVal(name, value)
64
+
65
+ await provider.open()
66
+ const source = provider.source()
67
+
68
+ expect(source).toStrictEqual({ foo: { bar: 'foo_' + value } })
69
+ })
70
+
71
+ it('should replace multiple placeholders', async () => {
72
+ const configuration = { foo: '${FOO} ${BAR}' }
73
+
74
+ setEnv(configuration)
75
+ setVal('FOO', 'hello')
76
+ setVal('BAR', 'world')
77
+
78
+ await provider.open()
79
+ const source = provider.source()
80
+
81
+ expect(source).toStrictEqual({ foo: 'hello world' })
82
+ })
83
+
84
+ it('should not replace if variable not set', async () => {
85
+ const configuration = { foo: '${FOO}' }
86
+
87
+ setEnv(configuration)
88
+
89
+ await provider.open()
90
+ const value = provider.source()
91
+
92
+ expect(value).toStrictEqual(configuration)
93
+ })
94
+
55
95
  const usedVariables = []
56
96
 
57
97
  /**
@@ -60,8 +100,9 @@ const usedVariables = []
60
100
  */
61
101
  function setEnv (configuration, secrets) {
62
102
  const variable = PREFIX + locator.uppercase
103
+ const encoded = encode(configuration)
63
104
 
64
- setVal(variable, configuration)
105
+ setVal(variable, encoded)
65
106
 
66
107
  if (secrets !== undefined) {
67
108
  for (const [key, value] of Object.entries(secrets)) {
@@ -74,7 +115,7 @@ function setEnv (configuration, secrets) {
74
115
  }
75
116
 
76
117
  function setVal (variable, value) {
77
- process.env[variable] = encode(value)
118
+ process.env[variable] = value
78
119
  usedVariables.push(variable)
79
120
  }
80
121