@toa.io/extensions.configuration 0.20.0-dev.34 → 0.20.0-dev.36

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.
Files changed (51) hide show
  1. package/package.json +16 -9
  2. package/readme.md +19 -27
  3. package/schemas/annotation.cos.yaml +1 -0
  4. package/schemas/manifest.cos.yaml +2 -0
  5. package/source/Aspect.test.ts +15 -0
  6. package/source/Aspect.ts +23 -0
  7. package/source/Factory.ts +12 -0
  8. package/source/configuration.test.ts +89 -0
  9. package/source/configuration.ts +52 -0
  10. package/source/deployment.test.ts +21 -0
  11. package/source/deployment.ts +69 -0
  12. package/source/index.ts +3 -0
  13. package/source/manifest.test.ts +15 -0
  14. package/source/manifest.ts +15 -0
  15. package/source/schemas.ts +8 -0
  16. package/tsconfig.json +9 -0
  17. package/docs/discussion.md +0 -109
  18. package/source/.deployment/index.js +0 -7
  19. package/source/.deployment/secrets.js +0 -35
  20. package/source/.deployment/variables.js +0 -23
  21. package/source/.manifest/.normalize/verbose.js +0 -51
  22. package/source/.manifest/index.js +0 -7
  23. package/source/.manifest/normalize.js +0 -16
  24. package/source/.manifest/schema.yaml +0 -9
  25. package/source/.manifest/validate.js +0 -13
  26. package/source/.provider/env.js +0 -19
  27. package/source/.provider/form.js +0 -20
  28. package/source/.provider/index.js +0 -7
  29. package/source/annotation.fixtures.js +0 -39
  30. package/source/annotation.js +0 -36
  31. package/source/annotation.test.js +0 -43
  32. package/source/aspect.fixtures.js +0 -32
  33. package/source/aspect.js +0 -36
  34. package/source/aspect.test.js +0 -76
  35. package/source/configuration.js +0 -19
  36. package/source/deployment.fixtures.js +0 -38
  37. package/source/deployment.js +0 -20
  38. package/source/deployment.test.js +0 -70
  39. package/source/factory.js +0 -39
  40. package/source/factory.test.js +0 -22
  41. package/source/index.js +0 -13
  42. package/source/manifest.js +0 -13
  43. package/source/manifest.test.js +0 -123
  44. package/source/provider.js +0 -119
  45. package/source/provider.test.js +0 -130
  46. package/source/secrets.js +0 -28
  47. package/source/secrets.test.js +0 -47
  48. package/types/aspect.d.ts +0 -11
  49. package/types/factory.d.ts +0 -12
  50. package/types/provider.d.ts +0 -22
  51. /package/{docs → notes}/consistency.md +0 -0
@@ -1,51 +0,0 @@
1
- 'use strict'
2
-
3
- const { remap } = require('@toa.io/generic')
4
-
5
- const verbose = (node) => {
6
- const isObject = node.properties !== undefined
7
- const isValue = node.type !== undefined && node.type !== 'object'
8
- const isProperties = node[SYM] === 1
9
-
10
- if (!isObject && !isValue && !isProperties) return convert(node)
11
-
12
- return node
13
- }
14
-
15
- const convert = (node) => {
16
- const properties = remap(node, property)
17
-
18
- properties[SYM] = 1
19
-
20
- return { type: 'object', properties, additionalProperties: false }
21
- }
22
-
23
- function property (node) {
24
- if (node === null) return { type: 'null', default: null }
25
-
26
- const type = Array.isArray(node) ? 'array' : typeof node
27
-
28
- if (type === 'object') return node
29
- if (type === 'array') return array(node)
30
-
31
- return { type, default: node }
32
- }
33
-
34
- const array = (array) => {
35
- if (array.length === 0) throw new Error('Configuration: cannot resolve concise array items type because it\'s empty')
36
-
37
- const type = typeof array[0]
38
-
39
- const schema = {
40
- type: 'array',
41
- default: array
42
- }
43
-
44
- if (array.length === 1) schema.items = array[0]
45
-
46
- return schema
47
- }
48
-
49
- const SYM = Symbol()
50
-
51
- exports.verbose = verbose
@@ -1,7 +0,0 @@
1
- 'use strict'
2
-
3
- const { normalize } = require('./normalize')
4
- const { validate } = require('./validate')
5
-
6
- exports.normalize = normalize
7
- exports.validate = validate
@@ -1,16 +0,0 @@
1
- 'use strict'
2
-
3
- const { traverse } = require('@toa.io/generic')
4
- const { verbose } = require('./.normalize/verbose')
5
-
6
- const normalize = (manifest) => {
7
- if (manifest.properties === undefined) manifest = expand(manifest)
8
-
9
- return manifest
10
- }
11
-
12
- const expand = (concise) => {
13
- return traverse(concise, verbose)
14
- }
15
-
16
- exports.normalize = normalize
@@ -1,9 +0,0 @@
1
- $schema: https://json-schema.org/draft/2019-09/schema
2
- $id: https://schemas.toa.io/0.0.0/extensions/configuration/manifest
3
-
4
- $ref: 'https://schemas.toa.io/0.0.0/definitions#/definitions/schema'
5
- type: object
6
- properties:
7
- type:
8
- const: object
9
- default: object
@@ -1,13 +0,0 @@
1
- 'use strict'
2
-
3
- const path = require('path')
4
-
5
- const { Schema } = require('@toa.io/schema')
6
- const { load } = require('@toa.io/yaml')
7
-
8
- const schema = load.sync(path.resolve(__dirname, 'schema.yaml'))
9
- const validator = new Schema(schema)
10
-
11
- const validate = (declaration) => validator.validate(declaration)
12
-
13
- exports.validate = validate
@@ -1,19 +0,0 @@
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
@@ -1,20 +0,0 @@
1
- 'use strict'
2
-
3
- const { traverse } = require('@toa.io/generic')
4
-
5
- /**
6
- * @param {toa.schema.JSON | Object} schema
7
- * @return {Object}
8
- */
9
- const form = (schema) => {
10
- const defaults = (node) => {
11
- if (node.type === 'object' && node.properties !== undefined) return { ...node.properties }
12
- if (node.default !== undefined) return node.default
13
-
14
- return null
15
- }
16
-
17
- return traverse(schema, defaults)
18
- }
19
-
20
- exports.form = form
@@ -1,7 +0,0 @@
1
- 'use strict'
2
-
3
- const { form } = require('./form')
4
- const { env } = require('./env')
5
-
6
- exports.form = form
7
- exports.env = env
@@ -1,39 +0,0 @@
1
- 'use strict'
2
-
3
- const { generate } = require('randomstring')
4
- const { Locator } = require('@toa.io/core')
5
- const { random } = require('@toa.io/generic')
6
-
7
- const instance = () => {
8
- const name = generate()
9
- const namespace = generate()
10
- const locator = new Locator(name, namespace)
11
-
12
- const manifest = {
13
- type: 'object',
14
- properties: {
15
- foo: {
16
- type: 'number',
17
- default: 1
18
- }
19
- }
20
- }
21
-
22
- return { locator, manifest }
23
- }
24
-
25
- /** @type {toa.norm.context.dependencies.Instance[]} */
26
- const instances = []
27
-
28
- for (let i = 0; i < random(5) + 5; i++) instances.push(instance())
29
-
30
- /** @type {Object} */
31
- const annotation = {}
32
-
33
- for (let i = 0; i < random(instances.length - 1) + 1; i++) {
34
- const instance = instances[i]
35
- annotation[instance.locator.id] = { foo: random() }
36
- }
37
-
38
- exports.instances = instances
39
- exports.annotation = annotation
@@ -1,36 +0,0 @@
1
- 'use strict'
2
-
3
- const { Schema } = require('@toa.io/schema')
4
-
5
- /**
6
- * @param {Object} annotation
7
- * @param {toa.norm.context.dependencies.Instance[]} instances
8
- */
9
- const annotation = (annotation, instances) => {
10
- const keys = Object.keys(annotation)
11
-
12
- check(keys, instances)
13
-
14
- for (const instance of instances) {
15
- const object = annotation[instance.locator.id] ?? {}
16
- const schema = new Schema(instance.manifest)
17
-
18
- schema.validate(object)
19
- }
20
-
21
- return annotation
22
- }
23
-
24
- /**
25
- * @param {string[]} keys
26
- * @param {toa.norm.context.dependencies.Instance[]} instances
27
- */
28
- const check = (keys, instances) => {
29
- const ids = instances.map((instance) => instance.locator.id)
30
-
31
- for (const key of keys) {
32
- if (!ids.includes(key)) throw new Error(`Configuration Schema '${key}' is not defined`)
33
- }
34
- }
35
-
36
- exports.annotation = annotation
@@ -1,43 +0,0 @@
1
- 'use strict'
2
-
3
- const clone = require('clone-deep')
4
- const { generate } = require('randomstring')
5
- const { sample } = require('@toa.io/generic')
6
-
7
- const fixtures = require('./annotation.fixtures')
8
- const { annotation } = require('../')
9
-
10
- let input
11
- /** @type {toa.norm.context.dependencies.Instance[]} */ let instances
12
-
13
- const call = () => annotation(input, instances)
14
-
15
- beforeEach(() => {
16
- input = clone(fixtures.annotation)
17
- instances = clone(fixtures.instances)
18
- })
19
-
20
- it('sample must be valid', () => {
21
- expect(call).not.toThrow()
22
- })
23
-
24
- it('should must be a function', () => {
25
- expect(annotation).toBeDefined()
26
- expect(annotation).toBeInstanceOf(Function)
27
- })
28
-
29
- it('should throw on non-existent component', () => {
30
- input[generate()] = {}
31
-
32
- expect(call).toThrow(/Configuration Schema/)
33
- })
34
-
35
- it('should throw if object doesn\'t match schema', () => {
36
- const keys = Object.keys(input)
37
- const key = sample(keys)
38
- const object = input[key]
39
-
40
- object.foo = generate()
41
-
42
- expect(call).toThrow(/foo must be number/)
43
- })
@@ -1,32 +0,0 @@
1
- 'use strict'
2
-
3
- const { generate } = require('randomstring')
4
-
5
- const schema = {
6
- type: 'object',
7
- properties: {
8
- foo: {
9
- type: 'string',
10
- default: generate()
11
- },
12
- bar: {
13
- type: 'object',
14
- properties: {
15
- baz: {
16
- type: 'number',
17
- default: 1
18
- }
19
- }
20
- }
21
- }
22
- }
23
-
24
- const concise = {
25
- foo: schema.properties.foo.default,
26
- bar: {
27
- baz: schema.properties.bar.properties.baz.default
28
- }
29
- }
30
-
31
- exports.schema = schema
32
- exports.concise = concise
package/source/aspect.js DELETED
@@ -1,36 +0,0 @@
1
- 'use strict'
2
-
3
- const { Connector } = require('@toa.io/core')
4
-
5
- /**
6
- * @implements {toa.extensions.configuration.Aspect}
7
- */
8
- class Aspect extends Connector {
9
- /** @readonly */
10
- name = 'configuration'
11
-
12
- /** @type {toa.core.Reflection} */
13
- #refection
14
-
15
- /**
16
- * @param {toa.core.Reflection} reflection
17
- */
18
- constructor (reflection) {
19
- super()
20
-
21
- this.#refection = reflection
22
-
23
- this.depends(reflection)
24
- }
25
-
26
- invoke (path) {
27
- /** @type {any} */
28
- let cursor = this.#refection.value
29
-
30
- if (path !== undefined) for (const segment of path) cursor = cursor[segment]
31
-
32
- return cursor
33
- }
34
- }
35
-
36
- exports.Aspect = Aspect
@@ -1,76 +0,0 @@
1
- 'use strict'
2
-
3
- const { Locator } = require('@toa.io/core')
4
- const { encode } = require('@toa.io/generic')
5
-
6
- const fixtures = require('./aspect.fixtures')
7
- const { Factory } = require('../')
8
- const { generate } = require('randomstring')
9
-
10
- const factory = new Factory()
11
-
12
- /** @type {toa.extensions.configuration.Aspect} */
13
- let aspect
14
-
15
- /** @type {toa.core.Locator} */
16
- let locator
17
-
18
- describe('defaults', () => {
19
- beforeEach(async () => {
20
- const namespace = generate()
21
- const name = generate()
22
-
23
- locator = new Locator(name, namespace)
24
-
25
- aspect = factory.aspect(locator, fixtures.schema)
26
-
27
- await aspect.connect()
28
- })
29
-
30
- it('should return schema defaults', () => {
31
- const foo = aspect.invoke(['foo'])
32
-
33
- expect(foo).toStrictEqual(fixtures.schema.properties.foo.default)
34
- })
35
-
36
- it('should return nested values', () => {
37
- const baz = aspect.invoke(['bar', 'baz'])
38
-
39
- expect(baz).toStrictEqual(fixtures.schema.properties.bar.properties.baz.default)
40
- })
41
-
42
- it('should expose configuration tree', () => {
43
- const configuration = aspect.invoke()
44
-
45
- expect(configuration).toStrictEqual({
46
- foo: fixtures.schema.properties.foo.default,
47
- bar: {
48
- baz: fixtures.schema.properties.bar.properties.baz.default
49
- }
50
- })
51
- })
52
- })
53
-
54
- describe('resolution', () => {
55
- let object
56
- let varname
57
-
58
- beforeEach(() => {
59
- object = { foo: generate() }
60
-
61
- varname = 'TOA_CONFIGURATION_' + locator.uppercase
62
- })
63
-
64
- it('should resolve configuration object from environment variable', async () => {
65
- process.env[varname] = encode(object)
66
-
67
- aspect = factory.aspect(locator, fixtures.schema)
68
-
69
- await aspect.connect()
70
-
71
- const configuration = aspect.invoke()
72
-
73
- expect(configuration.foo).toStrictEqual(object.foo)
74
- expect(configuration.bar.baz).toStrictEqual(fixtures.schema.properties.bar.properties.baz.default)
75
- })
76
- })
@@ -1,19 +0,0 @@
1
- 'use strict'
2
-
3
- const { Reflection } = require('@toa.io/core')
4
-
5
- /**
6
- * @implements {toa.core.Reflection}
7
- */
8
- class Configuration extends Reflection {
9
- /**
10
- * @param {toa.extensions.configuration.Provider} provider
11
- */
12
- constructor (provider) {
13
- super(provider.source)
14
-
15
- this.depends(provider)
16
- }
17
- }
18
-
19
- exports.Configuration = Configuration
@@ -1,38 +0,0 @@
1
- 'use strict'
2
-
3
- const { generate } = require('randomstring')
4
-
5
- const { Locator } = require('@toa.io/core')
6
- const { random } = require('@toa.io/generic')
7
-
8
- const component = () => {
9
- const namespace = generate()
10
- const name = generate()
11
-
12
- return { locator: new Locator(name, namespace) }
13
- }
14
-
15
- /** @type {toa.norm.context.dependencies.Instance[]} */
16
- const components = []
17
- const annotations = {}
18
-
19
- const annotate = (component) => {
20
- const key = component.locator.id
21
-
22
- annotations[key] = { [generate()]: generate() }
23
- }
24
-
25
- for (let i = 0; i < random(10) + 5; i++) components.push(component())
26
- for (let i = 0; i < components.length; i++) if (i % 2 === 0) annotate(components[i])
27
-
28
- /**
29
- * @param {string} id
30
- * @returns {toa.norm.context.dependencies.Instance}
31
- */
32
- const find = (id) => {
33
- return components.find((component) => component.locator.id === id)
34
- }
35
-
36
- exports.components = components
37
- exports.annotations = annotations
38
- exports.find = find
@@ -1,20 +0,0 @@
1
- 'use strict'
2
-
3
- const { merge } = require('@toa.io/generic')
4
- const get = require('./.deployment')
5
-
6
- /**
7
- * @param {toa.norm.context.dependencies.Instance[]} components
8
- * @param {object} annotations
9
- * @return {toa.deployment.dependency.Declaration}
10
- */
11
- const deployment = (components, annotations) => {
12
- const variables = get.variables(components, annotations)
13
- const secrets = get.secrets(components, annotations)
14
-
15
- merge(variables, secrets)
16
-
17
- return { variables }
18
- }
19
-
20
- exports.deployment = deployment
@@ -1,70 +0,0 @@
1
- 'use strict'
2
-
3
- const clone = require('clone-deep')
4
- const { encode, sample } = require('@toa.io/generic')
5
-
6
- const fixtures = require('./deployment.fixtures')
7
- const { deployment } = require('../')
8
- const { generate } = require('randomstring')
9
-
10
- /** @type {toa.deployment.dependency.Declaration} */
11
- let declaration
12
-
13
- beforeAll(() => {
14
- declaration = deployment(fixtures.components, fixtures.annotations)
15
- })
16
-
17
- it('should exist', () => {
18
- expect(deployment).toBeDefined()
19
- })
20
-
21
- it('should declare variables', () => {
22
- expect(declaration.variables).toBeDefined()
23
- })
24
-
25
- it('should map configurations', () => {
26
- const keys = Object.keys(fixtures.annotations)
27
-
28
- expect(keys.length).toBeGreaterThan(0)
29
-
30
- for (const [id, annotations] of Object.entries(fixtures.annotations)) {
31
- const component = fixtures.find(id)
32
- const variables = declaration.variables[component.locator.label]
33
- const encoded = encode(annotations)
34
-
35
- expect(component).toBeDefined()
36
- expect(variables).toBeDefined()
37
- expect(variables).toBeInstanceOf(Array)
38
- expect(variables.length).toStrictEqual(1)
39
-
40
- const env = variables[0]
41
-
42
- expect(env.name).toBeDefined()
43
- expect(env.name).toStrictEqual('TOA_CONFIGURATION_' + component.locator.uppercase)
44
- expect(env.value).toBeDefined()
45
- expect(env.value).toStrictEqual(encoded)
46
- }
47
- })
48
-
49
- it('should declare secrets', async () => {
50
- const annotations = clone(fixtures.annotations)
51
- const component = sample(fixtures.components)
52
- const id = component.locator.id
53
- const key = generate()
54
- const name = generate().substring(0, 16).toUpperCase()
55
- const value = '$' + name
56
-
57
- if (annotations[id] === undefined) annotations[id] = {}
58
-
59
- annotations[id][key] = value
60
-
61
- declaration = deployment(fixtures.components, annotations)
62
-
63
- const variables = declaration.variables[component.locator.label]
64
-
65
- expect(variables).toBeDefined()
66
-
67
- const secret = variables.find((variable) => variable.name === 'TOA_CONFIGURATION__' + name)
68
-
69
- expect(secret).toBeDefined()
70
- })
package/source/factory.js DELETED
@@ -1,39 +0,0 @@
1
- 'use strict'
2
-
3
- const { Schema } = require('@toa.io/schema')
4
- const { Aspect } = require('./aspect')
5
- const { Configuration } = require('./configuration')
6
- const { Provider } = require('./provider')
7
-
8
- /**
9
- * @implements {toa.extensions.configuration.Factory}
10
- */
11
- class Factory {
12
- /**
13
- * @param {toa.core.Locator} locator
14
- * @param {toa.schema.JSON | Object} annotation
15
- * @return {toa.extensions.configuration.Aspect}
16
- */
17
- aspect (locator, annotation) {
18
- const schema = new Schema(annotation)
19
- const provider = new Provider(locator, schema)
20
- const configuration = new Configuration(provider)
21
-
22
- return new Aspect(configuration)
23
- }
24
-
25
- provider (component) {
26
- const locator = component.locator
27
- const declaration = component.extensions?.[ID]
28
-
29
- if (declaration === undefined) throw new Error(`Configuration extension not found in '${locator.id}'`)
30
-
31
- const schema = new Schema(declaration)
32
-
33
- return new Provider(locator, schema)
34
- }
35
- }
36
-
37
- const ID = require('../package.json').name
38
-
39
- exports.Factory = Factory
@@ -1,22 +0,0 @@
1
- 'use strict'
2
-
3
- const { Factory } = require('../')
4
-
5
- it('should export', () => {
6
- expect(Factory).toBeInstanceOf(Function)
7
- })
8
-
9
- /** @type {toa.extensions.configuration.Factory} */
10
- let factory
11
-
12
- beforeAll(async () => {
13
- factory = new Factory()
14
- })
15
-
16
- it('should expose context', () => {
17
- expect(factory.aspect).toBeInstanceOf(Function)
18
- })
19
-
20
- it('should expose provider', () => {
21
- expect(factory.provider).toBeInstanceOf(Function)
22
- })
package/source/index.js DELETED
@@ -1,13 +0,0 @@
1
- 'use strict'
2
-
3
- const { manifest } = require('./manifest')
4
- const { annotation } = require('./annotation')
5
- const { deployment } = require('./deployment')
6
-
7
- const { Factory } = require('./factory')
8
-
9
- exports.manifest = manifest
10
- exports.annotation = annotation
11
- exports.deployment = deployment
12
-
13
- exports.Factory = Factory
@@ -1,13 +0,0 @@
1
- 'use strict'
2
-
3
- const { normalize, validate } = require('./.manifest')
4
-
5
- const manifest = (manifest) => {
6
- const declaration = normalize(manifest)
7
-
8
- validate(declaration)
9
-
10
- return declaration
11
- }
12
-
13
- exports.manifest = manifest