@@ -46,44 +46,75 @@ deployments. The configuration is a key-value map describing each individual fea
4646 type : string
4747 fallbackValue : info
4848 appUrl : \.cfapps\.sap\.hana\.ondemand\.com$
49- validation : ^(?:error|warn|info|verbose|debug)$
49+ validations :
50+ - regex : ^(error|warn|info|verbose|debug)$
5051` ` `
5152
5253The semantics of these properties are as follows.
5354
5455| property | required | meaning |
5556| :------------ | :------- | :--------------------------------------------------------------- |
56- | active | | if this is ` false`, the corresponding feature toggle is inactive |
5757| type | true | one of the allowed types ` boolean`, `number`, `string` |
58- | fallbackValue | true | see below |
59- | appUrl | | see below |
60- | validation | | regex for input validation |
61- | allowedScopes | | see below |
58+ | fallbackValue | true | emergency fallback value |
59+ | active | | if this is `false`, the corresponding feature toggle is inactive |
60+ | appUrl | | activate toggle only if appUrl regex is matched |
61+ | validations | | list of validations |
62+
63+ _type_<br>
64+ You can use the type `string` to encode complex data types like arrays or objects, but need to take care of
65+ serialization/deserialization yourself. In these cases, make sure to use [external validation](#external-validation)
66+ so that new values can be deserialized correctly.
6267
6368_fallbackValue_<br>
6469This value gets set initially when the feature toggle is introduced, and it is also used as a fallback when
6570communication with Redis is interrupted during startup.
6671
72+ _active_<br>
73+ Using _active_ or _appUrl_ to block the activation of a feature toggle, will cause all usage code reading it to
74+ always get the fallback value.
75+
6776_appUrl_<br>
68- Regex for activating feature toggle _only_ if the cf app's url matches
77+ Regular expression for activating a feature toggle _only_ if at least one of its Cloud Foundry application's urls
78+ match. When the library is not running in `CF_REDIS` [integration mode](#integration-mode), this check is disabled.
79+ Here are some examples :
6980
70- - for CANARY landscape `\.cfapps\.sap\.hana\.ondemand\.com$`
71- - for EU10 landscape `\.cfapps\.eu10\.hana\.ondemand\.com$`
72- - specific CANARY app `<cf-app-name>\.cfapps\.sap\.hana\.ondemand\.com$`
81+ - for CANARY landscape `\.cfapps\.sap\.hana\.ondemand\.com$`,
82+ - for EU10 landscape `\.cfapps\.eu10\.hana\.ondemand\.com$`,
83+ - specific CANARY app `<cf-app-name>\.cfapps\.sap\.hana\.ondemand\.com$`.
7384
74- _allowedScopes_ <br>
75- This is an additional form of change validation. AllowedScopes can be set to a list of strings, for example
76- `allowedScopes : [tenant, user]`. With this configuration only matching scopes can be used when setting feature toggle
77- values.
85+ _validations_ <br>
86+ List of validations that will guard all changes of the associated feature toggle. All validations must pass
87+ successfully for a change to occur. Each kind of validation can happen multiple times. Here is a practical example with
88+ all possible validation kinds :
7889
79- {: .info }
80- You can use the type `string` to encode more complex data types, like arrays or objects, but need to take care of the
81- serialization/deserialization yourself. In these cases, make sure to use [external validation](#external-validation)
82- so that new values can be deserialized correctly.
90+ ` ` ` yaml
91+ # info: check api priority; 0 means access is disabled
92+ /check/priority:
93+ type: number
94+ fallbackValue: 0
95+ validations:
96+ - scopes: [user, tenant]
97+ - regex: ^\d +$
98+ - { module: "$CONFIG_DIR/validators.js", call: validateTenantScope }
99+ ` ` `
83100
84- {: .warn }
85- When using _active_ or _appUrl_ to block activation of a feature toggle, then user code accessing the
86- feature toggle value will always get the fallback value.
101+ The semantics of these properties are as follows.
102+
103+ | property | meaning |
104+ | :------- | :----------------------------------------- |
105+ | scopes | restrict which scopes are allowed |
106+ | regex | value converted to string must match regex |
107+ | module | register external validation module |
108+
109+ _module_<br>
110+ Module points to a module, where an [external validation](#external-validation) is implemented. These external checks
111+ get registered during initialization and will be called during change attempts. You can specify just the module and
112+ export the validation function directly. Alternatively, you can specify both the module and a property to call on the
113+ module.
114+
115+ For the module path, you can specify it either relative to the runtime working directory (usually the project root),
116+ e.g., `module : ./path-from-root/validations.js`, or you can use the location of the configuration file as a relative
117+ anchor, e.g., `module : $CONFIG_DIR/validation.js`.
87118
88119# # Initialization for CAP Projects
89120
@@ -162,6 +193,23 @@ const config = await readConfigFromFile(FEATURES_FILEPATH);
162193await initializeFeatures({ config });
163194` ` `
164195
196+ # # Integration Mode
197+
198+ After successful initialization, the library will write one info log of the form :
199+
200+ ` ` `
201+ 13:40:13.775 | INFO | /FeatureToggles | finished initialization with 2 feature toggles with NO_REDIS
202+ ` ` `
203+
204+ It tells you both how many toggles where initialized, and the integration mode, that the library detected. Here are all
205+ possible modes :
206+
207+ | mode | meaning |
208+ | :------------ | :------------------------------------------------------------------------------- |
209+ | `NO_REDIS` | no redis detected, all changes are only in memory and will be lost on restart |
210+ | `LOCAL_REDIS` | local redis server detected, changes are persisted in that local redis |
211+ | `CF_REDIS` | Cloud Foundry redis service binding detected, changes will be persisted normally |
212+
165213# # Environment Variables
166214
167215The following environment variables can be used to fine-tune the library's behavior :
@@ -177,9 +225,9 @@ The following environment variables can be used to fine-tune the library's behav
177225In this section, we will assume that the [initialization](#initialization) has happened and the configuration contained
178226a feature toggle with the key `/srv/util/logger/logLevel`, similar to the one described [here](#format).
179227
180- # ## Querying Feature Value
228+ # ## Reading Feature Value
181229
182- You can query the current in memory state of any feature toggle :
230+ You can read the current in memory state of any feature toggle :
183231
184232` ` ` javascript
185233const {
@@ -247,10 +295,38 @@ async function changeIt(newValue, scopeMap) {
247295The change API `changeFeatureValue` will return when the change is published to Redis, so there may be a slight
248296processing delay until the change is picked up by all subscribers.
249297
250- {: .info }
251298Setting a feature value to `null` will delete the associated remote state and effectively reset it to its fallback
252299value.
253300
301+ Since setting values for scope-combinations happens additively, it can become hard to keep track of which combinations
302+ have dedicated values attached to them. If you want to set a value _and_ make sure that there isn't a more specific
303+ scope-combination, which overrides that value, then you can use the option `{ clearSubScopes : true }` as a third
304+ argument. For example
305+
306+ ` ` ` javascript
307+ await changeFeatureValue("/srv/util/logger/logLevel", "error", {}, { clearSubScopes: true });
308+ ` ` `
309+
310+ will set the root-scope value to `"error"` and remove all sub-scopes. See
311+ [scoping]({{ base_url }}/architecture/#scoping) for context.
312+
313+ # ## Resetting Feature Value
314+
315+ There is a convenience reset API just to reset a feature toggle and remove all associated persisted values. Reading
316+ the feature toggle afterward will only yield the fallback value until new changes are made.
317+
318+ ` ` ` javascript
319+ const {
320+ singleton: { resetFeatureValue, changeFeatureValue },
321+ } = require("@cap-js-community/feature-toggle-library");
322+
323+ // ... in some function
324+ await resetFeatureValue("/srv/util/logger/logLevel");
325+
326+ // this is functionally equivalent to
327+ await changeFeatureValue("/srv/util/logger/logLevel", null, {}, { clearSubScopes: true });
328+ ` ` `
329+
254330# ## External Validation
255331
256332The `string`-type feature toggles can theoretically encode very complex data structures, so it's sensible to validate
0 commit comments