Skip to content

Commit 5ca71e1

Browse files
authored
Fb/config override docs (#102)
* change version to 1.3.0 and update changelog * more concise wording * add configuration as concept * add configuration as concept 2 * add configuration as concept 3 * add configuration as concept 4 * add configuration as concept 5 * add configuration as concept 6 * add configuration as concept 7 * add configuration as concept 8 * add configuration as concept 9 * add configuration as concept 10
1 parent 06fe0f4 commit 5ca71e1

File tree

9 files changed

+161
-15
lines changed

9 files changed

+161
-15
lines changed

CHANGELOG.md

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,22 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.
77

88
<!-- order is REMOVED, CHANGED, ADDED, FIXED -->
99

10-
## v1.2.6 - tbd
10+
## v1.3.0 - tbd
11+
12+
### Changed
13+
14+
- core: initialization throws if mandatory configuration `fallbackValue` is `undefined` (missing) or `null` (always
15+
invalid).
16+
17+
- core: initialization throws if mandatory configuration `type` is not in `["boolean", "number", "string"]` (invalid).
18+
19+
- core: initialization throws if its invoked more than once. the reason is that the options passed in subsequent calls
20+
were always ignored.
21+
22+
- core: the configuration reading and aggregation has changed:
23+
- up to `v1.2.5`, the order was `runtime`, `files`, `auto`, where the _first occurrence_ is used for each toggle
24+
- from `v1.3.0`, the order is `auto`, `files`, `runtime`, where the _last occurrence_ is used for each toggle
25+
- this enables overriding the configuration based on environmental factors, by mixing in dedicated config files
1126

1227
## v1.2.5 - 2025-02-20
1328

Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
<mxfile host="Electron" agent="Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) draw.io/27.0.9 Chrome/134.0.6998.205 Electron/35.4.0 Safari/537.36" version="27.0.9">
2+
<diagram name="Page-1" id="IgljjqXF11xHlQnlV1vm">
3+
<mxGraphModel dx="2066" dy="2388" grid="1" gridSize="10" guides="1" tooltips="1" connect="1" arrows="1" fold="1" page="1" pageScale="1" pageWidth="827" pageHeight="1169" math="0" shadow="0">
4+
<root>
5+
<mxCell id="0" />
6+
<mxCell id="1" parent="0" />
7+
<mxCell id="ghchXokQqMajBl4t-yi9-3" value="" style="endArrow=none;dashed=1;html=1;rounded=0;strokeWidth=1;" parent="1" edge="1">
8+
<mxGeometry width="50" height="50" relative="1" as="geometry">
9+
<mxPoint x="160" y="-40" as="sourcePoint" />
10+
<mxPoint x="160" y="240" as="targetPoint" />
11+
</mxGeometry>
12+
</mxCell>
13+
<mxCell id="QjxQf7h-RPmqD-Jpzl0V-6" value="" style="endArrow=none;dashed=1;html=1;rounded=0;strokeWidth=1;" parent="1" edge="1">
14+
<mxGeometry width="50" height="50" relative="1" as="geometry">
15+
<mxPoint x="480" y="-40" as="sourcePoint" />
16+
<mxPoint x="480" y="240" as="targetPoint" />
17+
</mxGeometry>
18+
</mxCell>
19+
<mxCell id="QjxQf7h-RPmqD-Jpzl0V-7" value="" style="endArrow=none;html=1;rounded=0;strokeWidth=1;" parent="1" edge="1">
20+
<mxGeometry width="50" height="50" relative="1" as="geometry">
21+
<mxPoint x="640" y="-40" as="sourcePoint" />
22+
<mxPoint x="640" y="240" as="targetPoint" />
23+
</mxGeometry>
24+
</mxCell>
25+
<mxCell id="QjxQf7h-RPmqD-Jpzl0V-1" value="Auto" style="text;html=1;strokeColor=none;fillColor=none;align=center;verticalAlign=middle;whiteSpace=wrap;rounded=0;fontStyle=1;fontSize=14;" parent="1" vertex="1">
26+
<mxGeometry x="50" y="-30" width="60" height="30" as="geometry" />
27+
</mxCell>
28+
<mxCell id="QjxQf7h-RPmqD-Jpzl0V-2" value="File A" style="text;html=1;strokeColor=none;fillColor=none;align=center;verticalAlign=middle;whiteSpace=wrap;rounded=0;fontStyle=1;fontSize=14;" parent="1" vertex="1">
29+
<mxGeometry x="229" y="-30" width="60" height="30" as="geometry" />
30+
</mxCell>
31+
<mxCell id="QjxQf7h-RPmqD-Jpzl0V-3" value="File B" style="text;html=1;strokeColor=none;fillColor=none;align=center;verticalAlign=middle;whiteSpace=wrap;rounded=0;fontStyle=1;fontSize=14;" parent="1" vertex="1">
32+
<mxGeometry x="349" y="-30" width="60" height="30" as="geometry" />
33+
</mxCell>
34+
<mxCell id="QjxQf7h-RPmqD-Jpzl0V-4" value="Runtime" style="text;html=1;strokeColor=none;fillColor=none;align=center;verticalAlign=middle;whiteSpace=wrap;rounded=0;fontStyle=1;fontSize=14;" parent="1" vertex="1">
35+
<mxGeometry x="532" y="-30" width="60" height="30" as="geometry" />
36+
</mxCell>
37+
<mxCell id="QjxQf7h-RPmqD-Jpzl0V-5" value="Actual" style="text;html=1;strokeColor=none;fillColor=none;align=center;verticalAlign=middle;whiteSpace=wrap;rounded=0;fontStyle=1;fontSize=14;" parent="1" vertex="1">
38+
<mxGeometry x="710" y="-30" width="60" height="30" as="geometry" />
39+
</mxCell>
40+
<mxCell id="QjxQf7h-RPmqD-Jpzl0V-8" value="" style="endArrow=none;html=1;rounded=0;strokeWidth=1;" parent="1" edge="1">
41+
<mxGeometry width="50" height="50" relative="1" as="geometry">
42+
<mxPoint x="10" as="sourcePoint" />
43+
<mxPoint x="820" as="targetPoint" />
44+
</mxGeometry>
45+
</mxCell>
46+
<mxCell id="sAZw095eo5NpUbxJhi_2-5" value="Toggle 1" style="text;html=1;strokeColor=none;fillColor=none;align=center;verticalAlign=middle;whiteSpace=wrap;rounded=0;" parent="1" vertex="1">
47+
<mxGeometry x="50" y="20" width="60" height="30" as="geometry" />
48+
</mxCell>
49+
<mxCell id="QjxQf7h-RPmqD-Jpzl0V-11" value="Toggle 1" style="text;html=1;strokeColor=none;fillColor=none;align=center;verticalAlign=middle;whiteSpace=wrap;rounded=0;" parent="1" vertex="1">
50+
<mxGeometry x="229" y="20" width="60" height="30" as="geometry" />
51+
</mxCell>
52+
<mxCell id="QjxQf7h-RPmqD-Jpzl0V-12" value="Toggle 1" style="text;html=1;strokeColor=none;fillColor=none;align=center;verticalAlign=middle;whiteSpace=wrap;rounded=0;" parent="1" vertex="1">
53+
<mxGeometry x="532" y="20" width="60" height="30" as="geometry" />
54+
</mxCell>
55+
<mxCell id="QjxQf7h-RPmqD-Jpzl0V-13" value="Toggle 1&lt;br&gt;from Runtime" style="text;html=1;strokeColor=none;fillColor=none;align=center;verticalAlign=middle;whiteSpace=wrap;rounded=0;" parent="1" vertex="1">
56+
<mxGeometry x="690" y="20" width="100" height="30" as="geometry" />
57+
</mxCell>
58+
<mxCell id="QjxQf7h-RPmqD-Jpzl0V-14" value="Toggle 2" style="text;html=1;strokeColor=none;fillColor=none;align=center;verticalAlign=middle;whiteSpace=wrap;rounded=0;" parent="1" vertex="1">
59+
<mxGeometry x="50" y="70" width="60" height="30" as="geometry" />
60+
</mxCell>
61+
<mxCell id="QjxQf7h-RPmqD-Jpzl0V-15" value="Toggle 2&lt;br&gt;from Auto" style="text;html=1;strokeColor=none;fillColor=none;align=center;verticalAlign=middle;whiteSpace=wrap;rounded=0;" parent="1" vertex="1">
62+
<mxGeometry x="690" y="70" width="100" height="30" as="geometry" />
63+
</mxCell>
64+
<mxCell id="QjxQf7h-RPmqD-Jpzl0V-16" value="Toggle 3" style="text;html=1;strokeColor=none;fillColor=none;align=center;verticalAlign=middle;whiteSpace=wrap;rounded=0;" parent="1" vertex="1">
65+
<mxGeometry x="229" y="120" width="60" height="30" as="geometry" />
66+
</mxCell>
67+
<mxCell id="QjxQf7h-RPmqD-Jpzl0V-17" value="Toggle 3" style="text;html=1;strokeColor=none;fillColor=none;align=center;verticalAlign=middle;whiteSpace=wrap;rounded=0;" parent="1" vertex="1">
68+
<mxGeometry x="349" y="120" width="60" height="30" as="geometry" />
69+
</mxCell>
70+
<mxCell id="QjxQf7h-RPmqD-Jpzl0V-18" value="Toggle 3&lt;br&gt;from File B" style="text;html=1;strokeColor=none;fillColor=none;align=center;verticalAlign=middle;whiteSpace=wrap;rounded=0;" parent="1" vertex="1">
71+
<mxGeometry x="710" y="120" width="60" height="30" as="geometry" />
72+
</mxCell>
73+
<mxCell id="QjxQf7h-RPmqD-Jpzl0V-20" value="Toggle 4" style="text;html=1;strokeColor=none;fillColor=none;align=center;verticalAlign=middle;whiteSpace=wrap;rounded=0;" parent="1" vertex="1">
74+
<mxGeometry x="349" y="170" width="60" height="30" as="geometry" />
75+
</mxCell>
76+
<mxCell id="QjxQf7h-RPmqD-Jpzl0V-21" value="Toggle 4&lt;br&gt;from Runtime" style="text;html=1;strokeColor=none;fillColor=none;align=center;verticalAlign=middle;whiteSpace=wrap;rounded=0;" parent="1" vertex="1">
77+
<mxGeometry x="700" y="170" width="80" height="30" as="geometry" />
78+
</mxCell>
79+
<mxCell id="QjxQf7h-RPmqD-Jpzl0V-22" value="Toggle 4" style="text;html=1;strokeColor=none;fillColor=none;align=center;verticalAlign=middle;whiteSpace=wrap;rounded=0;" parent="1" vertex="1">
80+
<mxGeometry x="532" y="170" width="60" height="30" as="geometry" />
81+
</mxCell>
82+
<mxCell id="wsoxGE0fdQEzw4RMzBQQ-12" value="..." style="text;html=1;strokeColor=none;fillColor=none;align=center;verticalAlign=middle;whiteSpace=wrap;rounded=0;fontStyle=1;fontSize=14;" vertex="1" parent="1">
83+
<mxGeometry x="409" y="-33" width="60" height="30" as="geometry" />
84+
</mxCell>
85+
</root>
86+
</mxGraphModel>
87+
</diagram>
88+
</mxfile>
20.5 KB
Loading

docs/concepts/index.md

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,44 @@ Initialization broadly has this workflow:
3232
After initialization, usage code can rely on always getting at least the fallback values (including invalid values) or,
3333
if possible, validated values from Redis.
3434

35+
## Layered Configuration
36+
37+
We define three layers for toggle configurations: auto, files, and runtime. You can think of each layer as one or more
38+
Javascript objects that map feature toggle keys to their respective configurations.
39+
40+
- _auto_: are configurations recognized automatically _before initialization_. This layer is meant to be used for the
41+
CDS modelling feature toggles of the form `/fts/<feature-name>`. For details see
42+
[Feature Vector Provider]({{ site.baseurl }}/plugin/#feature-vector-provider).
43+
- _files_: are configuration files read _during initialization_. Files can be either in YAML or JSON format. For
44+
details see [Configuration Usage]({{ site.baseurl }}/usage/#configuration). This layer takes precedence over auto
45+
and the order of the files determines their precedence, with the _last occurrence_ for each toggle overriding
46+
previous occurrences.
47+
- _runtime_: are configurations passed just-in-time for initialization. This layer takes precedence over auto and
48+
files.
49+
50+
The relevant configuration setting for each level is listed here:
51+
52+
| layer | cds-plugin mode: cds.env.featureToggles | library mode: initialization(options) |
53+
| ------- | --------------------------------------- | ------------------------------------- |
54+
| auto | happens automatically | `options.configAuto` |
55+
| files | `featureToggles.configFile` or | `options.configFile` or |
56+
| | `featureToggles.configFiles` | `options.configFiles` |
57+
| runtime | `featureToggles.config` | `options.config` |
58+
59+
This layered approach allows you to override toggle configurations at each level. For a given toggle, the _last
60+
occurrence_ of its configuration will override all previous occurrences. To make the actual configuration clear, you
61+
can use the `/rest/feature/state`, or `/rest/feature/redisRead` endpoints, or their underlying library counterpart
62+
`toggles.getFeaturesInfos()`.
63+
64+
| ![](concepts-configuration.png) |
65+
| :-----------------------------: |
66+
| _Layered Configuration_ |
67+
68+
{: .warn }
69+
The override of a specific toggle configuration is never partial. In other words, you need to define all intended
70+
properties, for example _validations_, for each occurrence. This makes all individual toggle configurations
71+
_self-contained_.
72+
3573
## Single Key Approach
3674

3775
| ![](concepts-single-key.png) |

docs/plugin/index.md

Lines changed: 11 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -18,16 +18,17 @@ nav_order: 4
1818
Here is a list of all plugin settings that can be used in `package.json` under this library's node
1919
`cds.featureToggles`. All settings are optional.
2020

21-
| setting | type | meaning |
22-
| :----------------- | :----- | :------------------------------------------------------------------------ |
23-
| configFile | string | path of the [configuration]({{ site.baseurl }}/usage/#configuration) file |
24-
| config | object | inline configuration (only recommended for small projects) |
25-
| uniqueName | string | key name on redis, see below |
26-
| serviceAccessRoles | array | read and write access roles, see below |
27-
| readAccessRoles | array | see below |
28-
| writeAccessRoles | array | see below |
29-
| adminAccessRoles | array | see below |
30-
| ftsScopeCallback | string | custom scopes for cap feature toggles, see below |
21+
| setting | type | meaning |
22+
| :----------------- | :-------------- | :----------------------------------------------------------------------------------------- |
23+
| configFile | string | path of the [configuration]({{ site.baseurl }}/usage/#configuration) file |
24+
| configFiles | array or object | paths of [layered configuration]({{ site.baseurl }}/concepts/#layered-configuration) files |
25+
| config | object | inline configuration (only recommended for small projects) |
26+
| uniqueName | string | key name on redis, see below |
27+
| serviceAccessRoles | array | read and write access roles, see below |
28+
| readAccessRoles | array | see below |
29+
| writeAccessRoles | array | see below |
30+
| adminAccessRoles | array | see below |
31+
| ftsScopeCallback | string | custom scopes for cap feature toggles, see below |
3132

3233
_uniqueName_<br>
3334
The unique name is an identifier for the state data in redis. This defaults to the cloud foundry application name and

docs/usage/index.md

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,10 @@ back all toggles to their fallback values.
4141
## Configuration
4242

4343
We recommend maintaining the configuration in a _version-tracked_, YAML- or JSON-file, which only changes during
44-
deployments. The configuration is a key-value map describing each individual feature toggle. Here is an example in YAML.
44+
deployments. For complex projects, you can also make use of a
45+
[layered configuration]({{ site.baseurl }}/concepts/#layered-configuration).
46+
47+
The configuration is a key-value map describing each individual feature toggle. Here is an example in YAML.
4548

4649
```yaml
4750
/srv/util/logger/logLevel:

package-lock.json

Lines changed: 2 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@cap-js-community/feature-toggle-library",
3-
"version": "1.2.6",
3+
"version": "1.3.0",
44
"description": "SAP BTP feature toggle library enables Node.js applications using the SAP Cloud Application Programming Model to maintain live-updatable feature toggles via Redis.",
55
"main": "src/index.js",
66
"files": [

src/feature-toggles.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -991,6 +991,7 @@ class FeatureToggles {
991991
* @type object
992992
* @property {Config} [config]
993993
* @property {string} [configFile]
994+
* @property {string} [configFiles]
994995
* @property {Config} [configAuto]
995996
* @property {object} [customRedisCredentials]
996997
* @property {object} [customRedisClientOptions]

0 commit comments

Comments
 (0)